agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH v2 2/7] Row pattern recognition patch (parse/analysis). 261+ messages / 2 participants [nested] [flat]
* [PATCH v2 2/7] Row pattern recognition patch (parse/analysis). @ 2023-06-26 08:05 Tatsuo Ishii <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Tatsuo Ishii @ 2023-06-26 08:05 UTC (permalink / raw) --- src/backend/parser/parse_agg.c | 7 ++ src/backend/parser/parse_clause.c | 192 +++++++++++++++++++++++++++++- src/backend/parser/parse_expr.c | 4 + src/backend/parser/parse_func.c | 3 + 4 files changed, 205 insertions(+), 1 deletion(-) diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 85cd47b7ae..aa7a1cee80 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -564,6 +564,10 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) errkind = true; break; + case EXPR_KIND_RPR_DEFINE: + errkind = true; + break; + /* * There is intentionally no default: case here, so that the * compiler will warn if we add a new ParseExprKind without @@ -953,6 +957,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, case EXPR_KIND_CYCLE_MARK: errkind = true; break; + case EXPR_KIND_RPR_DEFINE: + errkind = true; + break; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index f61f794755..ccf3332bef 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -100,7 +100,10 @@ static WindowClause *findWindowClause(List *wclist, const char *name); static Node *transformFrameOffset(ParseState *pstate, int frameOptions, Oid rangeopfamily, Oid rangeopcintype, Oid *inRangeFunc, Node *clause); - +static void transformRPR(ParseState *pstate, WindowClause *wc, WindowDef *windef); +static List *transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef); +static List *transformPatternClause(ParseState *pstate, WindowClause *wc, WindowDef *windef); +static List *transformMeasureClause(ParseState *pstate, WindowClause *wc, WindowDef *windef); /* * transformFromClause - @@ -2949,6 +2952,10 @@ transformWindowDefinitions(ParseState *pstate, rangeopfamily, rangeopcintype, &wc->endInRangeFunc, windef->endOffset); + + /* Process Row Pattern Recognition related clauses */ + transformRPR(pstate, wc, windef); + wc->runCondition = NIL; wc->winref = winref; @@ -3814,3 +3821,186 @@ transformFrameOffset(ParseState *pstate, int frameOptions, return node; } + +/* + * transformRPR + * Process Row Pattern Recognition related clauses + */ +static void +transformRPR(ParseState *pstate, WindowClause *wc, WindowDef *windef) +{ + /* Check Frame option. Frame must start at current row */ + + /* + * Window definition exists? + */ + if (windef == NULL) + return; + + /* + * Row Pattern Common Syntax clause exists? + */ + if (windef->rpCommonSyntax == NULL) + return; + + if ((wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW) == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("FRAME must start at current row when row patttern recognition is used"))); + + /* Transform AFTER MACH SKIP TO clause */ + wc->rpSkipTo = windef->rpCommonSyntax->rpSkipTo; + + /* Transform SEEK or INITIAL clause */ + wc->initial = windef->rpCommonSyntax->initial; + + /* Transform DEFINE clause into list of TargetEntry's */ + wc->defineClause = transformDefineClause(pstate, wc, windef); + + /* Check PATTERN clause and copy to patternClause */ + transformPatternClause(pstate, wc, windef); + + /* Transform MEASURE clause */ + transformMeasureClause(pstate, wc, windef); +} + +/* + * transformDefineClause Process DEFINE clause and transform ResTarget into + * list of TargetEntry. + * + * XXX we only support column reference in row pattern definition search + * condition, e.g. "price". <row pattern definition variable name>.<column + * reference> is not supported, e.g. "A.price". + */ +static List * +transformDefineClause(ParseState *pstate, WindowClause *wc, WindowDef *windef) +{ + ListCell *lc; + ResTarget *restarget, *r; + List *restargets; + + + /* + * If Row Definition Common Syntax exists, DEFINE clause must exist. + * (the raw parser should have already checked it.) + */ + Assert(windef->rpCommonSyntax->rpDefs != NULL); + + /* + * Check for duplicate row pattern definition variables. The standard + * requires that no two row pattern definition variable names shall be + * equivalent. + */ + restargets = NIL; + foreach(lc, windef->rpCommonSyntax->rpDefs) + { + char *name; + ListCell *l; + + restarget = (ResTarget *)lfirst(lc); + name = restarget->name; + + /* + * Make sure that row pattern definition search condition is a boolean + * expression. + */ + transformWhereClause(pstate, restarget->val, + EXPR_KIND_RPR_DEFINE, "DEFINE"); + + foreach(l, restargets) + { + char *n; + + r = (ResTarget *) lfirst(l); + n = r->name; + + if (!strcmp(n, name)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("row pattern definition variable name \"%s\" appears more than once in DEFINE clause", + name), + parser_errposition(pstate, exprLocation((Node *)r)))); + } + restargets = lappend(restargets, restarget); + } + list_free(restargets); + + return transformTargetList(pstate, windef->rpCommonSyntax->rpDefs, + EXPR_KIND_RPR_DEFINE); +} + +/* + * transformPatternClause + * Process PATTERN clause and return PATTERN clause in the raw parse tree + */ +static List * +transformPatternClause(ParseState *pstate, WindowClause *wc, WindowDef *windef) +{ + List *patterns; + ListCell *lc, *l; + + /* + * Row Pattern Common Syntax clause exists? + */ + if (windef->rpCommonSyntax == NULL) + return NULL; + + /* + * Primary row pattern variable names in PATTERN clause must appear in + * DEFINE clause as row pattern definition variable names. + */ + wc->patternVariable = NIL; + wc->patternRegexp = NIL; + foreach(lc, windef->rpCommonSyntax->rpPatterns) + { + A_Expr *a; + char *name; + char *regexp; + bool found = false; + + if (!IsA(lfirst(lc), A_Expr)) + ereport(ERROR, + errmsg("node type is not A_Expr")); + + a = (A_Expr *)lfirst(lc); + name = strVal(a->lexpr); + + foreach(l, windef->rpCommonSyntax->rpDefs) + { + ResTarget *restarget = (ResTarget *)lfirst(l); + + if (!strcmp(restarget->name, name)) + { + found = true; + break; + } + } + + if (!found) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("primary row pattern variable name \"%s\" does not appear in DEFINE clause", + name), + parser_errposition(pstate, exprLocation((Node *)a)))); + wc->patternVariable = lappend(wc->patternVariable, makeString(pstrdup(name))); + regexp = strVal(lfirst(list_head(a->name))); + wc->patternRegexp = lappend(wc->patternRegexp, makeString(pstrdup(regexp))); + } + return patterns; +} + +/* + * transformMeasureClause + * Process MEASURE clause + */ +static List * +transformMeasureClause(ParseState *pstate, WindowClause *wc, WindowDef *windef) +{ + if (windef->rowPatternMeasures == NIL) + return NIL; + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s","MEASURE clause is not supported yet"), + parser_errposition(pstate, exprLocation((Node *)windef->rowPatternMeasures)))); +} diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 346fd272b6..20231d9ec0 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -541,6 +541,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) case EXPR_KIND_COPY_WHERE: case EXPR_KIND_GENERATED_COLUMN: case EXPR_KIND_CYCLE_MARK: + case EXPR_KIND_RPR_DEFINE: /* okay */ break; @@ -1754,6 +1755,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) case EXPR_KIND_VALUES: case EXPR_KIND_VALUES_SINGLE: case EXPR_KIND_CYCLE_MARK: + case EXPR_KIND_RPR_DEFINE: /* okay */ break; case EXPR_KIND_CHECK_CONSTRAINT: @@ -3133,6 +3135,8 @@ ParseExprKindName(ParseExprKind exprKind) return "GENERATED AS"; case EXPR_KIND_CYCLE_MARK: return "CYCLE"; + case EXPR_KIND_RPR_DEFINE: + return "DEFINE"; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index b3f0b6a137..2ff3699538 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -2656,6 +2656,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location) case EXPR_KIND_CYCLE_MARK: errkind = true; break; + case EXPR_KIND_RPR_DEFINE: + errkind = true; + break; /* * There is intentionally no default: case here, so that the -- 2.25.1 ----Next_Part(Mon_Jun_26_17_45_07_2023_724)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="v2-0003-Row-pattern-recognition-patch-planner.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION @ 2026-04-20 09:38 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw) Although REPACK (CONCURRENTLY) uses replication slots, there is no concern that the slot will leak data of other users, because the MAINTAIN privilege on the table is required anyway; requiring REPLICATION is user-unfriendly without providing any actual protection. A related aspect is that the REPLICATION attribute is not needed to prevent REPACK from stealing slots from logical replication, since commit e76d8c749c31 made REPACK use a separate pool of replication slots. Because there are now successful concurrent repack runs in the regression tests, we're forced to run test_plan_advice under wal_level=replica. Author: Antonin Houska <[email protected]> Reported-by: Justin Pryzby <[email protected]> Reviewed-by: Chao Li <[email protected]> Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023 --- src/backend/commands/repack_worker.c | 1 - .../test_plan_advice/t/001_replan_regress.pl | 1 + src/test/regress/expected/cluster.out | 20 +++++++++++++++++-- src/test/regress/sql/cluster.sql | 11 ++++++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index b17edd771e2..e4a4860805b 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid) /* * Make sure we can use logical decoding. */ - CheckSlotPermissions(); CheckLogicalDecodingRequirements(true); /* diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl index 38ffa4d11ae..452b179a665 100644 --- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl +++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl @@ -18,6 +18,7 @@ $node->init(); # Set up our desired configuration. $node->append_conf('postgresql.conf', <<EOM); shared_preload_libraries='test_plan_advice' +wal_level=replica pg_plan_advice.always_explain_supplied_advice=false pg_plan_advice.feedback_warnings=true EOM diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index 6127b215a86..e17bc91fae1 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; ERROR: permission denied for table ptnowner +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; +ERROR: cannot process relation "ptnowner1" +HINT: Relation "ptnowner1" has no identity index. RESET SESSION AUTHORIZATION; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; @@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a ptnowner2 | t (3 rows) +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; + relname | ?column? +-----------+---------- + ptnowner | t + ptnowner1 | f + ptnowner2 | t +(3 rows) + DROP TABLE ptnowner; DROP ROLE regress_ptnowner; -- Test CLUSTER with external tuplesorting diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index d14063a9683..1f471a8821a 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart; DROP TABLE clstrpart; -- Ownership of partitions is checked -CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i); +CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i); CREATE INDEX ptnowner_i_idx ON ptnowner(i); CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1); -CREATE ROLE regress_ptnowner; +CREATE ROLE regress_ptnowner LOGIN; CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; ALTER TABLE ptnowner OWNER TO regress_ptnowner; CREATE TEMP TABLE ptnowner_oldnodes AS @@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS JOIN pg_class AS c ON c.oid=tree.relid; SET SESSION AUTHORIZATION regress_ptnowner; CLUSTER ptnowner USING ptnowner_i_idx; +-- still can't repack without a replica identity +ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT; +REPACK (CONCURRENTLY) ptnowner1; RESET SESSION AUTHORIZATION; +SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a + JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; DROP TABLE ptnowner; -- 2.47.3 --m6zy3l65ushq557m Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-REPACK-do-not-require-LOGIN-privileges.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
end of thread, other threads:[~2026-04-20 09:38 UTC | newest] Thread overview: 261+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2023-06-26 08:05 [PATCH v2 2/7] Row pattern recognition patch (parse/analysis). Tatsuo Ishii <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]> 2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro 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