agora inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feed[PATCH v15] Avoid orphaned objects dependencies
332+ messages / 5 participants
[nested] [flat]
* [PATCH v15] Avoid orphaned objects dependencies
@ 2024-03-29 15:43 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
0 siblings, 0 replies; 332+ messages in thread
From: Bertrand Drouvot @ 2024-03-29 15:43 UTC (permalink / raw)
It's currently possible to create orphaned objects dependencies, for example:
Scenario 1:
session 1: begin; drop schema schem;
session 2: create a function in the schema schem
session 1: commit;
With the above, the function created in session 2 would be linked to a non
existing schema.
Scenario 2:
session 1: begin; create a function in the schema schem
session 2: drop schema schem;
session 1: commit;
With the above, the function created in session 1 would be linked to a non
existing schema.
To avoid those scenarios, a new lock (that conflicts with a lock taken by DROP)
has been put in place before the dependencies are being recorded. With this in
place, the drop schema in scenario 2 would be locked.
Also, after the new lock attempt, the patch checks that the object still exists:
with this in place session 2 in scenario 1 would be locked and would report an
error once session 1 committs (that would not be the case should session 1 abort
the transaction).
If the object is dropped before the new lock attempt is triggered then the patch
would also report an error (but with less details).
The patch takes into account any type of objects except the ones that are pinned
(they are not droppable because the system requires it).
A special case is done for objects that belong to the RelationRelationId class.
For those, we should be in one of the two following cases that would already
prevent the relation to be dropped:
1. The relation is already locked (could be an existing relation or a relation
that we are creating).
2. The relation is protected indirectly (i.e an index protected by a lock on
its table, a table protected by a lock on a function that depends the table...)
To avoid any risks for the RelationRelationId class case, we acquire a lock if
there is none. That may add unnecessary lock for 2. but that's worth it.
The patch adds a few tests for some dependency cases (that would currently produce
orphaned objects):
- schema and function (as the above scenarios)
- alter a dependency (function and schema)
- function and arg type
- function and return type
- function and function
- domain and domain
- table and type
- server and foreign data wrapper
---
src/backend/catalog/aclchk.c | 1 +
src/backend/catalog/dependency.c | 212 ++++++++++++++++++
src/backend/catalog/heap.c | 7 +
src/backend/catalog/index.c | 26 +++
src/backend/catalog/objectaddress.c | 57 +++++
src/backend/catalog/pg_aggregate.c | 9 +
src/backend/catalog/pg_attrdef.c | 1 +
src/backend/catalog/pg_cast.c | 5 +
src/backend/catalog/pg_collation.c | 1 +
src/backend/catalog/pg_constraint.c | 26 +++
src/backend/catalog/pg_conversion.c | 2 +
src/backend/catalog/pg_depend.c | 40 +++-
src/backend/catalog/pg_operator.c | 19 ++
src/backend/catalog/pg_proc.c | 17 +-
src/backend/catalog/pg_publication.c | 7 +
src/backend/catalog/pg_range.c | 6 +
src/backend/catalog/pg_type.c | 39 ++++
src/backend/catalog/toasting.c | 1 +
src/backend/commands/alter.c | 4 +
src/backend/commands/amcmds.c | 1 +
src/backend/commands/cluster.c | 7 +
src/backend/commands/event_trigger.c | 1 +
src/backend/commands/extension.c | 5 +
src/backend/commands/foreigncmds.c | 7 +
src/backend/commands/functioncmds.c | 6 +
src/backend/commands/indexcmds.c | 2 +
src/backend/commands/opclasscmds.c | 18 ++
src/backend/commands/operatorcmds.c | 30 +++
src/backend/commands/policy.c | 2 +
src/backend/commands/proclang.c | 3 +
src/backend/commands/sequence.c | 2 +
src/backend/commands/statscmds.c | 10 +
src/backend/commands/tablecmds.c | 34 ++-
src/backend/commands/trigger.c | 29 ++-
src/backend/commands/tsearchcmds.c | 73 +++++-
src/backend/commands/typecmds.c | 84 +++++++
src/backend/rewrite/rewriteDefine.c | 1 +
src/backend/utils/errcodes.txt | 1 +
src/include/catalog/dependency.h | 3 +
src/include/catalog/objectaddress.h | 1 +
.../expected/test_dependencies_locks.out | 129 +++++++++++
src/test/isolation/isolation_schedule | 1 +
.../specs/test_dependencies_locks.spec | 89 ++++++++
.../test_oat_hooks/expected/alter_table.out | 4 +-
.../expected/test_oat_hooks.out | 2 +
src/test/regress/expected/alter_table.out | 11 +-
46 files changed, 1011 insertions(+), 25 deletions(-)
39.6% src/backend/catalog/
30.5% src/backend/commands/
16.7% src/test/isolation/expected/
10.4% src/test/isolation/specs/
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index a44ccee3b6..9a24872a30 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -1413,6 +1413,7 @@ SetDefaultACL(InternalDefaultACL *iacls)
referenced.objectId = iacls->nspid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(NamespaceRelationId, iacls->nspid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0489cbabcb..a3770d7206 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1519,6 +1519,81 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
}
}
+/*
+ * LockNotPinnedObjectById
+ *
+ * Lock the object that we are about to record a dependency on.
+ * After it's locked, verify that it hasn't been dropped while we
+ * weren't looking. If the object has been dropped, this function
+ * does not return!
+ */
+void
+LockNotPinnedObjectById(const ObjectAddress *object)
+{
+ char *object_description = NULL;
+
+ if (isObjectPinned(object))
+ return;
+
+ object_description = getObjectDescription(object, true);
+
+ if (object->classId == RelationRelationId)
+ {
+ Assert(!IsSharedRelation(object->objectId));
+
+ /*
+ * We must be in one of the two following cases that would already
+ * prevent the relation to be dropped: 1. The relation is already
+ * locked (could be an existing relation or a relation that we are
+ * creating). 2. The relation is protected indirectly (i.e an index
+ * protected by a lock on its table, a table protected by a lock on a
+ * function that depends of the table...). To avoid any risks, acquire
+ * a lock if there is none. That may add unnecessary lock for 2. but
+ * that's worth it.
+ */
+ if (!CheckRelationOidLockedByMe(object->objectId, AccessShareLock, true))
+ LockRelationOid(object->objectId, AccessShareLock);
+ }
+ else
+ {
+ /* assume we should lock the whole object not a sub-object */
+ LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock);
+ }
+
+ /* check if object still exists */
+ if (!ObjectByIdExist(object))
+ {
+ if (object_description)
+ ereport(ERROR,
+ (errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+ errmsg("%s does not exist", object_description)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+ errmsg("a dependent object does not exist")));
+ }
+
+ if (object_description)
+ pfree(object_description);
+
+ return;
+}
+
+/*
+ * LockNotPinnedObject
+ *
+ * Lock the object that we are about to record a dependency on.
+ */
+void
+LockNotPinnedObject(Oid classid, Oid objid)
+{
+ ObjectAddress object;
+
+ ObjectAddressSet(object, classid, objid);
+
+ LockNotPinnedObjectById(&object);
+}
+
/*
* ReleaseDeletionLock - release an object deletion lock
*
@@ -1730,6 +1805,7 @@ find_expr_references_walker(Node *node,
if (rte->rtekind == RTE_RELATION)
{
/* If it's a plain relation, reference this column */
+ LockNotPinnedObject(RelationRelationId, rte->relid);
add_object_address(RelationRelationId, rte->relid, var->varattno,
context->addrs);
}
@@ -1756,6 +1832,7 @@ find_expr_references_walker(Node *node,
Oid objoid;
/* A constant must depend on the constant's datatype */
+ LockNotPinnedObject(TypeRelationId, con->consttype);
add_object_address(TypeRelationId, con->consttype, 0,
context->addrs);
@@ -1767,8 +1844,11 @@ find_expr_references_walker(Node *node,
*/
if (OidIsValid(con->constcollid) &&
con->constcollid != DEFAULT_COLLATION_OID)
+ {
+ LockNotPinnedObject(CollationRelationId, con->constcollid);
add_object_address(CollationRelationId, con->constcollid, 0,
context->addrs);
+ }
/*
* If it's a regclass or similar literal referring to an existing
@@ -1785,59 +1865,83 @@ find_expr_references_walker(Node *node,
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(PROCOID,
ObjectIdGetDatum(objoid)))
+ {
+ LockNotPinnedObject(ProcedureRelationId, objoid);
add_object_address(ProcedureRelationId, objoid, 0,
context->addrs);
+ }
break;
case REGOPEROID:
case REGOPERATOROID:
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(OPEROID,
ObjectIdGetDatum(objoid)))
+ {
+ LockNotPinnedObject(OperatorRelationId, objoid);
add_object_address(OperatorRelationId, objoid, 0,
context->addrs);
+ }
break;
case REGCLASSOID:
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(RELOID,
ObjectIdGetDatum(objoid)))
+ {
+ LockNotPinnedObject(RelationRelationId, objoid);
add_object_address(RelationRelationId, objoid, 0,
context->addrs);
+ }
break;
case REGTYPEOID:
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(TYPEOID,
ObjectIdGetDatum(objoid)))
+ {
+ LockNotPinnedObject(TypeRelationId, objoid);
add_object_address(TypeRelationId, objoid, 0,
context->addrs);
+ }
break;
case REGCOLLATIONOID:
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(COLLOID,
ObjectIdGetDatum(objoid)))
+ {
+ LockNotPinnedObject(CollationRelationId, objoid);
add_object_address(CollationRelationId, objoid, 0,
context->addrs);
+ }
break;
case REGCONFIGOID:
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(TSCONFIGOID,
ObjectIdGetDatum(objoid)))
+ {
+ LockNotPinnedObject(TSConfigRelationId, objoid);
add_object_address(TSConfigRelationId, objoid, 0,
context->addrs);
+ }
break;
case REGDICTIONARYOID:
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(TSDICTOID,
ObjectIdGetDatum(objoid)))
+ {
+ LockNotPinnedObject(TSDictionaryRelationId, objoid);
add_object_address(TSDictionaryRelationId, objoid, 0,
context->addrs);
+ }
break;
case REGNAMESPACEOID:
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(NAMESPACEOID,
ObjectIdGetDatum(objoid)))
+ {
+ LockNotPinnedObject(NamespaceRelationId, objoid);
add_object_address(NamespaceRelationId, objoid, 0,
context->addrs);
+ }
break;
/*
@@ -1859,18 +1963,23 @@ find_expr_references_walker(Node *node,
Param *param = (Param *) node;
/* A parameter must depend on the parameter's datatype */
+ LockNotPinnedObject(TypeRelationId, param->paramtype);
add_object_address(TypeRelationId, param->paramtype, 0,
context->addrs);
/* and its collation, just as for Consts */
if (OidIsValid(param->paramcollid) &&
param->paramcollid != DEFAULT_COLLATION_OID)
+ {
+ LockNotPinnedObject(CollationRelationId, param->paramcollid);
add_object_address(CollationRelationId, param->paramcollid, 0,
context->addrs);
+ }
}
else if (IsA(node, FuncExpr))
{
FuncExpr *funcexpr = (FuncExpr *) node;
+ LockNotPinnedObject(ProcedureRelationId, funcexpr->funcid);
add_object_address(ProcedureRelationId, funcexpr->funcid, 0,
context->addrs);
/* fall through to examine arguments */
@@ -1879,6 +1988,7 @@ find_expr_references_walker(Node *node,
{
OpExpr *opexpr = (OpExpr *) node;
+ LockNotPinnedObject(OperatorRelationId, opexpr->opno);
add_object_address(OperatorRelationId, opexpr->opno, 0,
context->addrs);
/* fall through to examine arguments */
@@ -1887,6 +1997,7 @@ find_expr_references_walker(Node *node,
{
DistinctExpr *distinctexpr = (DistinctExpr *) node;
+ LockNotPinnedObject(OperatorRelationId, distinctexpr->opno);
add_object_address(OperatorRelationId, distinctexpr->opno, 0,
context->addrs);
/* fall through to examine arguments */
@@ -1895,6 +2006,7 @@ find_expr_references_walker(Node *node,
{
NullIfExpr *nullifexpr = (NullIfExpr *) node;
+ LockNotPinnedObject(OperatorRelationId, nullifexpr->opno);
add_object_address(OperatorRelationId, nullifexpr->opno, 0,
context->addrs);
/* fall through to examine arguments */
@@ -1903,6 +2015,7 @@ find_expr_references_walker(Node *node,
{
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+ LockNotPinnedObject(OperatorRelationId, opexpr->opno);
add_object_address(OperatorRelationId, opexpr->opno, 0,
context->addrs);
/* fall through to examine arguments */
@@ -1911,6 +2024,7 @@ find_expr_references_walker(Node *node,
{
Aggref *aggref = (Aggref *) node;
+ LockNotPinnedObject(ProcedureRelationId, aggref->aggfnoid);
add_object_address(ProcedureRelationId, aggref->aggfnoid, 0,
context->addrs);
/* fall through to examine arguments */
@@ -1919,6 +2033,7 @@ find_expr_references_walker(Node *node,
{
WindowFunc *wfunc = (WindowFunc *) node;
+ LockNotPinnedObject(ProcedureRelationId, wfunc->winfnoid);
add_object_address(ProcedureRelationId, wfunc->winfnoid, 0,
context->addrs);
/* fall through to examine arguments */
@@ -1935,8 +2050,11 @@ find_expr_references_walker(Node *node,
*/
if (sbsref->refrestype != sbsref->refcontainertype &&
sbsref->refrestype != sbsref->refelemtype)
+ {
+ LockNotPinnedObject(TypeRelationId, sbsref->refrestype);
add_object_address(TypeRelationId, sbsref->refrestype, 0,
context->addrs);
+ }
/* fall through to examine arguments */
}
else if (IsA(node, SubPlan))
@@ -1960,16 +2078,25 @@ find_expr_references_walker(Node *node,
* anywhere else in the expression.
*/
if (OidIsValid(reltype))
+ {
+ LockNotPinnedObject(RelationRelationId, reltype);
add_object_address(RelationRelationId, reltype, fselect->fieldnum,
context->addrs);
+ }
else
+ {
+ LockNotPinnedObject(TypeRelationId, fselect->resulttype);
add_object_address(TypeRelationId, fselect->resulttype, 0,
context->addrs);
+ }
/* the collation might not be referenced anywhere else, either */
if (OidIsValid(fselect->resultcollid) &&
fselect->resultcollid != DEFAULT_COLLATION_OID)
+ {
+ LockNotPinnedObject(CollationRelationId, fselect->resultcollid);
add_object_address(CollationRelationId, fselect->resultcollid, 0,
context->addrs);
+ }
}
else if (IsA(node, FieldStore))
{
@@ -1980,53 +2107,76 @@ find_expr_references_walker(Node *node,
if (OidIsValid(reltype))
{
ListCell *l;
+ bool locked = false;
foreach(l, fstore->fieldnums)
+ {
+ if (!locked)
+ {
+ LockNotPinnedObject(RelationRelationId, reltype);
+ locked = true;
+ }
add_object_address(RelationRelationId, reltype, lfirst_int(l),
context->addrs);
+ }
}
else
+ {
+ LockNotPinnedObject(TypeRelationId, fstore->resulttype);
add_object_address(TypeRelationId, fstore->resulttype, 0,
context->addrs);
+ }
}
else if (IsA(node, RelabelType))
{
RelabelType *relab = (RelabelType *) node;
/* since there is no function dependency, need to depend on type */
+ LockNotPinnedObject(TypeRelationId, relab->resulttype);
add_object_address(TypeRelationId, relab->resulttype, 0,
context->addrs);
/* the collation might not be referenced anywhere else, either */
if (OidIsValid(relab->resultcollid) &&
relab->resultcollid != DEFAULT_COLLATION_OID)
+ {
+ LockNotPinnedObject(CollationRelationId, relab->resultcollid);
add_object_address(CollationRelationId, relab->resultcollid, 0,
context->addrs);
+ }
}
else if (IsA(node, CoerceViaIO))
{
CoerceViaIO *iocoerce = (CoerceViaIO *) node;
/* since there is no exposed function, need to depend on type */
+ LockNotPinnedObject(TypeRelationId, iocoerce->resulttype);
add_object_address(TypeRelationId, iocoerce->resulttype, 0,
context->addrs);
/* the collation might not be referenced anywhere else, either */
if (OidIsValid(iocoerce->resultcollid) &&
iocoerce->resultcollid != DEFAULT_COLLATION_OID)
+ {
+ LockNotPinnedObject(CollationRelationId, iocoerce->resultcollid);
add_object_address(CollationRelationId, iocoerce->resultcollid, 0,
context->addrs);
+ }
}
else if (IsA(node, ArrayCoerceExpr))
{
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
/* as above, depend on type */
+ LockNotPinnedObject(TypeRelationId, acoerce->resulttype);
add_object_address(TypeRelationId, acoerce->resulttype, 0,
context->addrs);
/* the collation might not be referenced anywhere else, either */
if (OidIsValid(acoerce->resultcollid) &&
acoerce->resultcollid != DEFAULT_COLLATION_OID)
+ {
+ LockNotPinnedObject(CollationRelationId, acoerce->resultcollid);
add_object_address(CollationRelationId, acoerce->resultcollid, 0,
context->addrs);
+ }
/* fall through to examine arguments */
}
else if (IsA(node, ConvertRowtypeExpr))
@@ -2034,6 +2184,7 @@ find_expr_references_walker(Node *node,
ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;
/* since there is no function dependency, need to depend on type */
+ LockNotPinnedObject(TypeRelationId, cvt->resulttype);
add_object_address(TypeRelationId, cvt->resulttype, 0,
context->addrs);
}
@@ -2041,6 +2192,7 @@ find_expr_references_walker(Node *node,
{
CollateExpr *coll = (CollateExpr *) node;
+ LockNotPinnedObject(CollationRelationId, coll->collOid);
add_object_address(CollationRelationId, coll->collOid, 0,
context->addrs);
}
@@ -2048,6 +2200,7 @@ find_expr_references_walker(Node *node,
{
RowExpr *rowexpr = (RowExpr *) node;
+ LockNotPinnedObject(TypeRelationId, rowexpr->row_typeid);
add_object_address(TypeRelationId, rowexpr->row_typeid, 0,
context->addrs);
}
@@ -2058,11 +2211,13 @@ find_expr_references_walker(Node *node,
foreach(l, rcexpr->opnos)
{
+ LockNotPinnedObject(OperatorRelationId, lfirst_oid(l));
add_object_address(OperatorRelationId, lfirst_oid(l), 0,
context->addrs);
}
foreach(l, rcexpr->opfamilies)
{
+ LockNotPinnedObject(OperatorFamilyRelationId, lfirst_oid(l));
add_object_address(OperatorFamilyRelationId, lfirst_oid(l), 0,
context->addrs);
}
@@ -2072,6 +2227,7 @@ find_expr_references_walker(Node *node,
{
CoerceToDomain *cd = (CoerceToDomain *) node;
+ LockNotPinnedObject(TypeRelationId, cd->resulttype);
add_object_address(TypeRelationId, cd->resulttype, 0,
context->addrs);
}
@@ -2079,6 +2235,7 @@ find_expr_references_walker(Node *node,
{
NextValueExpr *nve = (NextValueExpr *) node;
+ LockNotPinnedObject(RelationRelationId, nve->seqid);
add_object_address(RelationRelationId, nve->seqid, 0,
context->addrs);
}
@@ -2087,19 +2244,26 @@ find_expr_references_walker(Node *node,
OnConflictExpr *onconflict = (OnConflictExpr *) node;
if (OidIsValid(onconflict->constraint))
+ {
+ LockNotPinnedObject(ConstraintRelationId, onconflict->constraint);
add_object_address(ConstraintRelationId, onconflict->constraint, 0,
context->addrs);
+ }
/* fall through to examine arguments */
}
else if (IsA(node, SortGroupClause))
{
SortGroupClause *sgc = (SortGroupClause *) node;
+ LockNotPinnedObject(OperatorRelationId, sgc->eqop);
add_object_address(OperatorRelationId, sgc->eqop, 0,
context->addrs);
if (OidIsValid(sgc->sortop))
+ {
+ LockNotPinnedObject(OperatorRelationId, sgc->sortop);
add_object_address(OperatorRelationId, sgc->sortop, 0,
context->addrs);
+ }
return false;
}
else if (IsA(node, WindowClause))
@@ -2107,15 +2271,24 @@ find_expr_references_walker(Node *node,
WindowClause *wc = (WindowClause *) node;
if (OidIsValid(wc->startInRangeFunc))
+ {
+ LockNotPinnedObject(ProcedureRelationId, wc->startInRangeFunc);
add_object_address(ProcedureRelationId, wc->startInRangeFunc, 0,
context->addrs);
+ }
if (OidIsValid(wc->endInRangeFunc))
+ {
+ LockNotPinnedObject(ProcedureRelationId, wc->endInRangeFunc);
add_object_address(ProcedureRelationId, wc->endInRangeFunc, 0,
context->addrs);
+ }
if (OidIsValid(wc->inRangeColl) &&
wc->inRangeColl != DEFAULT_COLLATION_OID)
+ {
+ LockNotPinnedObject(CollationRelationId, wc->inRangeColl);
add_object_address(CollationRelationId, wc->inRangeColl, 0,
context->addrs);
+ }
/* fall through to examine substructure */
}
else if (IsA(node, CTECycleClause))
@@ -2123,14 +2296,23 @@ find_expr_references_walker(Node *node,
CTECycleClause *cc = (CTECycleClause *) node;
if (OidIsValid(cc->cycle_mark_type))
+ {
+ LockNotPinnedObject(TypeRelationId, cc->cycle_mark_type);
add_object_address(TypeRelationId, cc->cycle_mark_type, 0,
context->addrs);
+ }
if (OidIsValid(cc->cycle_mark_collation))
+ {
+ LockNotPinnedObject(CollationRelationId, cc->cycle_mark_collation);
add_object_address(CollationRelationId, cc->cycle_mark_collation, 0,
context->addrs);
+ }
if (OidIsValid(cc->cycle_mark_neop))
+ {
+ LockNotPinnedObject(OperatorRelationId, cc->cycle_mark_neop);
add_object_address(OperatorRelationId, cc->cycle_mark_neop, 0,
context->addrs);
+ }
/* fall through to examine substructure */
}
else if (IsA(node, Query))
@@ -2163,6 +2345,7 @@ find_expr_references_walker(Node *node,
switch (rte->rtekind)
{
case RTE_RELATION:
+ LockNotPinnedObject(RelationRelationId, rte->relid);
add_object_address(RelationRelationId, rte->relid, 0,
context->addrs);
break;
@@ -2215,12 +2398,18 @@ find_expr_references_walker(Node *node,
rte = rt_fetch(query->resultRelation, query->rtable);
if (rte->rtekind == RTE_RELATION)
{
+ bool locked = false;
foreach(lc, query->targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
if (tle->resjunk)
continue; /* ignore junk tlist items */
+ if (!locked)
+ {
+ LockNotPinnedObject(RelationRelationId, rte->relid);
+ locked = true;
+ }
add_object_address(RelationRelationId, rte->relid, tle->resno,
context->addrs);
}
@@ -2232,6 +2421,7 @@ find_expr_references_walker(Node *node,
*/
foreach(lc, query->constraintDeps)
{
+ LockNotPinnedObject(ConstraintRelationId, lfirst_oid(lc));
add_object_address(ConstraintRelationId, lfirst_oid(lc), 0,
context->addrs);
}
@@ -2266,16 +2456,25 @@ find_expr_references_walker(Node *node,
*/
foreach(ct, rtfunc->funccoltypes)
{
+ LockNotPinnedObject(TypeRelationId, lfirst_oid(ct));
add_object_address(TypeRelationId, lfirst_oid(ct), 0,
context->addrs);
}
foreach(ct, rtfunc->funccolcollations)
{
Oid collid = lfirst_oid(ct);
+ bool locked = false;
if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+ {
+ if (!locked)
+ {
+ LockNotPinnedObject(CollationRelationId, collid);
+ locked = true;
+ }
add_object_address(CollationRelationId, collid, 0,
context->addrs);
+ }
}
}
else if (IsA(node, TableFunc))
@@ -2288,22 +2487,32 @@ find_expr_references_walker(Node *node,
*/
foreach(ct, tf->coltypes)
{
+ LockNotPinnedObject(TypeRelationId, lfirst_oid(ct));
add_object_address(TypeRelationId, lfirst_oid(ct), 0,
context->addrs);
}
foreach(ct, tf->colcollations)
{
Oid collid = lfirst_oid(ct);
+ bool locked = false;
if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+ {
+ if (!locked)
+ {
+ LockNotPinnedObject(CollationRelationId, collid);
+ locked = true;
+ }
add_object_address(CollationRelationId, collid, 0,
context->addrs);
+ }
}
}
else if (IsA(node, TableSampleClause))
{
TableSampleClause *tsc = (TableSampleClause *) node;
+ LockNotPinnedObject(ProcedureRelationId, tsc->tsmhandler);
add_object_address(ProcedureRelationId, tsc->tsmhandler, 0,
context->addrs);
/* fall through to examine arguments */
@@ -2354,9 +2563,12 @@ process_function_rte_ref(RangeTblEntry *rte, AttrNumber attnum,
Assert(attnum - atts_done <= tupdesc->natts);
if (OidIsValid(reltype)) /* can this fail? */
+ {
+ LockNotPinnedObject(RelationRelationId, reltype);
add_object_address(RelationRelationId, reltype,
attnum - atts_done,
context->addrs);
+ }
return;
}
/* Nothing to do; function's result type is handled elsewhere */
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 00074c8a94..1266101d90 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -844,6 +844,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
/* Add dependency info */
ObjectAddressSubSet(myself, RelationRelationId, new_rel_oid, i + 1);
ObjectAddressSet(referenced, TypeRelationId, attr->atttypid);
+ LockNotPinnedObject(TypeRelationId, attr->atttypid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* The default collation is pinned, so don't bother recording it */
@@ -852,6 +853,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
{
ObjectAddressSet(referenced, CollationRelationId,
attr->attcollation);
+ LockNotPinnedObject(CollationRelationId, attr->attcollation);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
}
@@ -1459,11 +1461,13 @@ heap_create_with_catalog(const char *relname,
ObjectAddressSet(referenced, NamespaceRelationId, relnamespace);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(NamespaceRelationId, relnamespace);
if (reloftypeid)
{
ObjectAddressSet(referenced, TypeRelationId, reloftypeid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(TypeRelationId, reloftypeid);
}
/*
@@ -1477,6 +1481,7 @@ heap_create_with_catalog(const char *relname,
{
ObjectAddressSet(referenced, AccessMethodRelationId, accessmtd);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(AccessMethodRelationId, accessmtd);
}
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -3391,6 +3396,7 @@ StorePartitionKey(Relation rel,
{
ObjectAddressSet(referenced, OperatorClassRelationId, partopclass[i]);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(OperatorClassRelationId, partopclass[i]);
/* The default collation is pinned, so don't bother recording it */
if (OidIsValid(partcollation[i]) &&
@@ -3398,6 +3404,7 @@ StorePartitionKey(Relation rel,
{
ObjectAddressSet(referenced, CollationRelationId, partcollation[i]);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(CollationRelationId, partcollation[i]);
}
}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index a819b4197c..d6d1abfcf5 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1116,6 +1116,7 @@ index_create(Relation heapRelation,
else
{
bool have_simple_col = false;
+ bool locked_object = false;
addrs = new_object_addresses();
@@ -1128,6 +1129,12 @@ index_create(Relation heapRelation,
heapRelationId,
indexInfo->ii_IndexAttrNumbers[i]);
add_exact_object_address(&referenced, addrs);
+
+ if (!locked_object)
+ {
+ LockNotPinnedObject(RelationRelationId, heapRelationId);
+ locked_object = true;
+ }
have_simple_col = true;
}
}
@@ -1143,6 +1150,8 @@ index_create(Relation heapRelation,
ObjectAddressSet(referenced, RelationRelationId,
heapRelationId);
add_exact_object_address(&referenced, addrs);
+
+ LockNotPinnedObject(RelationRelationId, heapRelationId);
}
record_object_address_dependencies(&myself, addrs, DEPENDENCY_AUTO);
@@ -1158,9 +1167,13 @@ index_create(Relation heapRelation,
if (OidIsValid(parentIndexRelid))
{
ObjectAddressSet(referenced, RelationRelationId, parentIndexRelid);
+
+ LockNotPinnedObject(RelationRelationId, parentIndexRelid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
ObjectAddressSet(referenced, RelationRelationId, heapRelationId);
+
+ LockNotPinnedObject(RelationRelationId, heapRelationId);
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
}
@@ -1176,6 +1189,7 @@ index_create(Relation heapRelation,
{
ObjectAddressSet(referenced, CollationRelationId, collationIds[i]);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(CollationRelationId, collationIds[i]);
}
}
@@ -1184,6 +1198,7 @@ index_create(Relation heapRelation,
{
ObjectAddressSet(referenced, OperatorClassRelationId, opclassIds[i]);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(OperatorClassRelationId, opclassIds[i]);
}
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -1988,6 +2003,14 @@ index_constraint_create(Relation heapRelation,
*/
ObjectAddressSet(myself, ConstraintRelationId, conOid);
ObjectAddressSet(idxaddr, RelationRelationId, indexRelationId);
+
+ /*
+ * CommandCounterIncrement() here to ensure the new constraint entry is
+ * visible when LockNotPinnedObject() will check its existence before
+ * recording the dependencies.
+ */
+ CommandCounterIncrement();
+ LockNotPinnedObject(ConstraintRelationId, conOid);
recordDependencyOn(&idxaddr, &myself, DEPENDENCY_INTERNAL);
/*
@@ -1999,9 +2022,12 @@ index_constraint_create(Relation heapRelation,
ObjectAddress referenced;
ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId);
+ LockNotPinnedObject(ConstraintRelationId, parentConstraintId);
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
ObjectAddressSet(referenced, RelationRelationId,
RelationGetRelid(heapRelation));
+
+ LockNotPinnedObject(RelationRelationId, RelationGetRelid(heapRelation));
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
}
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2983b9180f..d3af0ae726 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2590,6 +2590,63 @@ get_object_namespace(const ObjectAddress *address)
return oid;
}
+/*
+ * ObjectByIdExist
+ *
+ * Return whether the given object exists.
+ *
+ * Works for most catalogs, if no special processing is needed.
+ */
+bool
+ObjectByIdExist(const ObjectAddress *address)
+{
+ HeapTuple tuple;
+ int cache;
+ const ObjectPropertyType *property;
+
+ property = get_object_property_data(address->classId);
+
+ cache = property->oid_catcache_id;
+
+ if (cache >= 0)
+ {
+ /* Fetch tuple from syscache. */
+ tuple = SearchSysCache1(cache, ObjectIdGetDatum(address->objectId));
+
+ if (!HeapTupleIsValid(tuple))
+ {
+ return false;
+ }
+
+ ReleaseSysCache(tuple);
+
+ return true;
+ }
+ else
+ {
+ Relation rel;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
+
+ rel = table_open(address->classId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ get_object_attnum_oid(address->classId),
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(address->objectId));
+
+ scan = systable_beginscan(rel, get_object_oid_index(address->classId), true,
+ NULL, 1, skey);
+
+ /* we expect exactly one match */
+ tuple = systable_getnext(scan);
+ systable_endscan(scan);
+ table_close(rel, AccessShareLock);
+
+ return (HeapTupleIsValid(tuple));
+ }
+}
+
/*
* Return ObjectType for the given object type as given by
* getObjectTypeDescription; if no valid ObjectType code exists, but it's a
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 90fc7db949..a47e3c5507 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -748,12 +748,14 @@ AggregateCreate(const char *aggName,
/* Depends on transition function */
ObjectAddressSet(referenced, ProcedureRelationId, transfn);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, transfn);
/* Depends on final function, if any */
if (OidIsValid(finalfn))
{
ObjectAddressSet(referenced, ProcedureRelationId, finalfn);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, finalfn);
}
/* Depends on combine function, if any */
@@ -761,6 +763,7 @@ AggregateCreate(const char *aggName,
{
ObjectAddressSet(referenced, ProcedureRelationId, combinefn);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, combinefn);
}
/* Depends on serialization function, if any */
@@ -768,6 +771,7 @@ AggregateCreate(const char *aggName,
{
ObjectAddressSet(referenced, ProcedureRelationId, serialfn);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, serialfn);
}
/* Depends on deserialization function, if any */
@@ -775,6 +779,7 @@ AggregateCreate(const char *aggName,
{
ObjectAddressSet(referenced, ProcedureRelationId, deserialfn);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, deserialfn);
}
/* Depends on forward transition function, if any */
@@ -782,6 +787,7 @@ AggregateCreate(const char *aggName,
{
ObjectAddressSet(referenced, ProcedureRelationId, mtransfn);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, mtransfn);
}
/* Depends on inverse transition function, if any */
@@ -789,6 +795,7 @@ AggregateCreate(const char *aggName,
{
ObjectAddressSet(referenced, ProcedureRelationId, minvtransfn);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, minvtransfn);
}
/* Depends on final function, if any */
@@ -796,6 +803,7 @@ AggregateCreate(const char *aggName,
{
ObjectAddressSet(referenced, ProcedureRelationId, mfinalfn);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, mfinalfn);
}
/* Depends on sort operator, if any */
@@ -803,6 +811,7 @@ AggregateCreate(const char *aggName,
{
ObjectAddressSet(referenced, OperatorRelationId, sortop);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(OperatorRelationId, sortop);
}
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c
index 003ae70b4d..dcce454f00 100644
--- a/src/backend/catalog/pg_attrdef.c
+++ b/src/backend/catalog/pg_attrdef.c
@@ -178,6 +178,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
colobject.objectId = RelationGetRelid(rel);
colobject.objectSubId = attnum;
+ LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
recordDependencyOn(&defobject, &colobject,
attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 5a5b855d51..d3707e424c 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -97,16 +97,19 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
/* dependency on source type */
ObjectAddressSet(referenced, TypeRelationId, sourcetypeid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(TypeRelationId, sourcetypeid);
/* dependency on target type */
ObjectAddressSet(referenced, TypeRelationId, targettypeid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(TypeRelationId, targettypeid);
/* dependency on function */
if (OidIsValid(funcid))
{
ObjectAddressSet(referenced, ProcedureRelationId, funcid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, funcid);
}
/* dependencies on casts required for function */
@@ -114,11 +117,13 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
{
ObjectAddressSet(referenced, CastRelationId, incastid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(CastRelationId, incastid);
}
if (OidIsValid(outcastid))
{
ObjectAddressSet(referenced, CastRelationId, outcastid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(CastRelationId, outcastid);
}
record_object_address_dependencies(&myself, addrs, behavior);
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index 7f2f701229..78498b8c20 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -218,6 +218,7 @@ CollationCreate(const char *collname, Oid collnamespace,
referenced.classId = NamespaceRelationId;
referenced.objectId = collnamespace;
referenced.objectSubId = 0;
+ LockNotPinnedObject(NamespaceRelationId, collnamespace);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* create dependency on owner */
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 3baf9231ed..c4cdbd7c58 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -252,17 +252,26 @@ CreateConstraintEntry(const char *constraintName,
if (constraintNTotalKeys > 0)
{
+ bool locked_object = false;
+
for (i = 0; i < constraintNTotalKeys; i++)
{
ObjectAddressSubSet(relobject, RelationRelationId, relId,
constraintKey[i]);
add_exact_object_address(&relobject, addrs_auto);
+
+ if (!locked_object)
+ {
+ LockNotPinnedObject(RelationRelationId, relId);
+ locked_object = true;
+ }
}
}
else
{
ObjectAddressSet(relobject, RelationRelationId, relId);
add_exact_object_address(&relobject, addrs_auto);
+ LockNotPinnedObject(RelationRelationId, relId);
}
}
@@ -275,6 +284,7 @@ CreateConstraintEntry(const char *constraintName,
ObjectAddressSet(domobject, TypeRelationId, domainId);
add_exact_object_address(&domobject, addrs_auto);
+ LockNotPinnedObject(TypeRelationId, domainId);
}
record_object_address_dependencies(&conobject, addrs_auto,
@@ -294,17 +304,26 @@ CreateConstraintEntry(const char *constraintName,
if (foreignNKeys > 0)
{
+ bool locked_object = false;
+
for (i = 0; i < foreignNKeys; i++)
{
ObjectAddressSubSet(relobject, RelationRelationId,
foreignRelId, foreignKey[i]);
add_exact_object_address(&relobject, addrs_normal);
+
+ if (!locked_object)
+ {
+ LockNotPinnedObject(RelationRelationId, foreignRelId);
+ locked_object = true;
+ }
}
}
else
{
ObjectAddressSet(relobject, RelationRelationId, foreignRelId);
add_exact_object_address(&relobject, addrs_normal);
+ LockNotPinnedObject(RelationRelationId, foreignRelId);
}
}
@@ -320,6 +339,7 @@ CreateConstraintEntry(const char *constraintName,
ObjectAddressSet(relobject, RelationRelationId, indexRelId);
add_exact_object_address(&relobject, addrs_normal);
+ LockNotPinnedObject(RelationRelationId, indexRelId);
}
if (foreignNKeys > 0)
@@ -339,15 +359,18 @@ CreateConstraintEntry(const char *constraintName,
{
oprobject.objectId = pfEqOp[i];
add_exact_object_address(&oprobject, addrs_normal);
+ LockNotPinnedObject(OperatorRelationId, pfEqOp[i]);
if (ppEqOp[i] != pfEqOp[i])
{
oprobject.objectId = ppEqOp[i];
add_exact_object_address(&oprobject, addrs_normal);
+ LockNotPinnedObject(OperatorRelationId, ppEqOp[i]);
}
if (ffEqOp[i] != pfEqOp[i])
{
oprobject.objectId = ffEqOp[i];
add_exact_object_address(&oprobject, addrs_normal);
+ LockNotPinnedObject(OperatorRelationId, ffEqOp[i]);
}
}
}
@@ -858,9 +881,12 @@ ConstraintSetParentConstraint(Oid childConstrId,
ObjectAddressSet(depender, ConstraintRelationId, childConstrId);
ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
+ LockNotPinnedObject(ConstraintRelationId, parentConstrId);
recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
ObjectAddressSet(referenced, RelationRelationId, childTableId);
+
+ LockNotPinnedObject(RelationRelationId, childTableId);
recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
}
else
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 0770878eac..25881654d6 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -116,12 +116,14 @@ ConversionCreate(const char *conname, Oid connamespace,
referenced.classId = ProcedureRelationId;
referenced.objectId = conproc;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ProcedureRelationId, conproc);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* create dependency on namespace */
referenced.classId = NamespaceRelationId;
referenced.objectId = connamespace;
referenced.objectSubId = 0;
+ LockNotPinnedObject(NamespaceRelationId, connamespace);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* create dependency on owner */
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index cfd7ef51df..ebca5a452b 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -20,21 +20,21 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/pg_auth_members.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_extension.h"
#include "catalog/partition.h"
#include "commands/extension.h"
#include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/lock.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/rel.h"
-static bool isObjectPinned(const ObjectAddress *object);
-
-
/*
* Record a dependency between 2 objects via their respective objectAddress.
* The first argument is the dependent object, the second the one it
@@ -100,6 +100,37 @@ recordMultipleDependencies(const ObjectAddress *depender,
slot_init_count = 0;
for (i = 0; i < nreferenced; i++, referenced++)
{
+#ifdef USE_ASSERT_CHECKING
+ if (!isObjectPinned(referenced))
+ {
+ if (referenced->classId != RelationRelationId)
+ {
+ LOCKTAG tag;
+
+ SET_LOCKTAG_OBJECT(tag,
+ MyDatabaseId,
+ referenced->classId,
+ referenced->objectId,
+ 0);
+ /* assert the referenced object is locked */
+ Assert(LockHeldByMe(&tag, AccessShareLock, false));
+ }
+ else
+ {
+ Assert(!IsSharedRelation(referenced->objectId));
+
+ /*
+ * Assert the referenced object is locked if it should be
+ * visible (see the comment related to LockNotPinnedObject()
+ * in TypeCreate()).
+ */
+ Assert(!ObjectByIdExist(referenced) ||
+ CheckRelationOidLockedByMe(referenced->objectId,
+ AccessShareLock, true));
+ }
+ }
+#endif
+
/*
* If the referenced object is pinned by the system, there's no real
* need to record dependencies on it. This saves lots of space in
@@ -239,6 +270,7 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
extension.objectId = CurrentExtensionObject;
extension.objectSubId = 0;
+ LockNotPinnedObject(ExtensionRelationId, CurrentExtensionObject);
recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
}
}
@@ -706,7 +738,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
* The passed subId, if any, is ignored; we assume that only whole objects
* are pinned (and that this implies pinning their components).
*/
-static bool
+bool
isObjectPinned(const ObjectAddress *object)
{
return IsPinnedObject(object->classId, object->objectId);
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 65b45a424a..e8374eec88 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -251,6 +251,16 @@ OperatorShellMake(const char *operatorName,
values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
+ /* Lock dependent objects */
+ if (OidIsValid(operatorNamespace))
+ LockNotPinnedObject(NamespaceRelationId, operatorNamespace);
+
+ if (OidIsValid(leftTypeId))
+ LockNotPinnedObject(TypeRelationId, leftTypeId);
+
+ if (OidIsValid(rightTypeId))
+ LockNotPinnedObject(TypeRelationId, rightTypeId);
+
/*
* create a new operator tuple
*/
@@ -513,6 +523,15 @@ OperatorCreate(const char *operatorName,
CatalogTupleInsert(pg_operator_desc, tup);
}
+ /* Lock dependent objects */
+ LockNotPinnedObject(NamespaceRelationId, operatorNamespace);
+ LockNotPinnedObject(TypeRelationId, leftTypeId);
+ LockNotPinnedObject(TypeRelationId, rightTypeId);
+ LockNotPinnedObject(TypeRelationId, operResultType);
+ LockNotPinnedObject(ProcedureRelationId, procedureId);
+ LockNotPinnedObject(ProcedureRelationId, restrictionId);
+ LockNotPinnedObject(ProcedureRelationId, joinId);
+
/* Add dependencies for the entry */
address = makeOperatorDependencies(tup, true, isUpdate);
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 528c17cd7f..116e524390 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -593,6 +593,13 @@ ProcedureCreate(const char *procedureName,
if (is_update)
deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
+ /*
+ * CommandCounterIncrement() here to ensure the new function entry is
+ * visible when LockNotPinnedObject() will check its existence before
+ * recording the dependencies.
+ */
+ CommandCounterIncrement();
+
addrs = new_object_addresses();
ObjectAddressSet(myself, ProcedureRelationId, retval);
@@ -600,20 +607,24 @@ ProcedureCreate(const char *procedureName,
/* dependency on namespace */
ObjectAddressSet(referenced, NamespaceRelationId, procNamespace);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(NamespaceRelationId, procNamespace);
/* dependency on implementation language */
ObjectAddressSet(referenced, LanguageRelationId, languageObjectId);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(LanguageRelationId, languageObjectId);
/* dependency on return type */
ObjectAddressSet(referenced, TypeRelationId, returnType);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(TypeRelationId, returnType);
/* dependency on transform used by return type, if any */
if ((trfid = get_transform_oid(returnType, languageObjectId, true)))
{
ObjectAddressSet(referenced, TransformRelationId, trfid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(TransformRelationId, trfid);
}
/* dependency on parameter types */
@@ -621,12 +632,14 @@ ProcedureCreate(const char *procedureName,
{
ObjectAddressSet(referenced, TypeRelationId, allParams[i]);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(TypeRelationId, allParams[i]);
/* dependency on transform used by parameter type, if any */
if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
{
ObjectAddressSet(referenced, TransformRelationId, trfid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(TransformRelationId, trfid);
}
}
@@ -635,6 +648,7 @@ ProcedureCreate(const char *procedureName,
{
ObjectAddressSet(referenced, ProcedureRelationId, prosupport);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, prosupport);
}
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -674,9 +688,6 @@ ProcedureCreate(const char *procedureName,
ArrayType *set_items = NULL;
int save_nestlevel = 0;
- /* Advance command counter so new tuple can be seen by validator */
- CommandCounterIncrement();
-
/*
* Set per-function configuration parameters so that the validation is
* done with the environment the function expects. However, if
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 0602398a54..b44a7f9d78 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -438,10 +438,13 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
/* Add dependency on the publication */
ObjectAddressSet(referenced, PublicationRelationId, pubid);
+ LockNotPinnedObject(PublicationRelationId, pubid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
/* Add dependency on the relation */
ObjectAddressSet(referenced, RelationRelationId, relid);
+
+ LockNotPinnedObject(RelationRelationId, relid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
/* Add dependency on the objects mentioned in the qualifications */
@@ -454,6 +457,8 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
for (int i = 0; i < natts; i++)
{
ObjectAddressSubSet(referenced, RelationRelationId, relid, attarray[i]);
+
+ LockNotPinnedObject(RelationRelationId, relid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
@@ -661,10 +666,12 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
/* Add dependency on the publication */
ObjectAddressSet(referenced, PublicationRelationId, pubid);
+ LockNotPinnedObject(PublicationRelationId, pubid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
/* Add dependency on the schema */
ObjectAddressSet(referenced, NamespaceRelationId, schemaid);
+ LockNotPinnedObject(NamespaceRelationId, schemaid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
/* Close the table */
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index 501a6ba410..e5b5a0b6f8 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -70,26 +70,31 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
ObjectAddressSet(referenced, TypeRelationId, rangeSubType);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(TypeRelationId, rangeSubType);
ObjectAddressSet(referenced, OperatorClassRelationId, rangeSubOpclass);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(OperatorClassRelationId, rangeSubOpclass);
if (OidIsValid(rangeCollation))
{
ObjectAddressSet(referenced, CollationRelationId, rangeCollation);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(CollationRelationId, rangeCollation);
}
if (OidIsValid(rangeCanonical))
{
ObjectAddressSet(referenced, ProcedureRelationId, rangeCanonical);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, rangeCanonical);
}
if (OidIsValid(rangeSubDiff))
{
ObjectAddressSet(referenced, ProcedureRelationId, rangeSubDiff);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, rangeSubDiff);
}
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -99,6 +104,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
referencing.classId = TypeRelationId;
referencing.objectId = multirangeTypeOid;
referencing.objectSubId = 0;
+ LockNotPinnedObject(TypeRelationId, rangeTypeOid);
recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
table_close(pg_range, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 395dec8ed8..82ee7bc2e3 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -157,6 +157,12 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
* Create dependencies. We can/must skip this in bootstrap mode.
*/
if (!IsBootstrapProcessingMode())
+ {
+ /* Lock dependent objects */
+ LockNotPinnedObject(NamespaceRelationId, typeNamespace);
+ LockNotPinnedObject(ProcedureRelationId, F_SHELL_IN);
+ LockNotPinnedObject(ProcedureRelationId, F_SHELL_OUT);
+
GenerateTypeDependencies(tup,
pg_type_desc,
NULL,
@@ -166,6 +172,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
false,
true, /* make extension dependency */
false);
+ }
/* Post creation hook for new shell type */
InvokeObjectPostCreateHook(TypeRelationId, typoid, 0);
@@ -494,6 +501,37 @@ TypeCreate(Oid newTypeOid,
* Create dependencies. We can/must skip this in bootstrap mode.
*/
if (!IsBootstrapProcessingMode())
+ {
+ /*
+ * CommandCounterIncrement() here to ensure the new type entry is
+ * visible when LockNotPinnedObject() will check its existence before
+ * recording the dependencies.
+ */
+ CommandCounterIncrement();
+
+ /* Lock dependent objects */
+ LockNotPinnedObject(NamespaceRelationId, typeNamespace);
+ LockNotPinnedObject(ProcedureRelationId, inputProcedure);
+ LockNotPinnedObject(ProcedureRelationId, outputProcedure);
+ LockNotPinnedObject(ProcedureRelationId, receiveProcedure);
+ LockNotPinnedObject(ProcedureRelationId, sendProcedure);
+ LockNotPinnedObject(ProcedureRelationId, typmodinProcedure);
+ LockNotPinnedObject(ProcedureRelationId, typmodoutProcedure);
+ LockNotPinnedObject(ProcedureRelationId, analyzeProcedure);
+ LockNotPinnedObject(ProcedureRelationId, subscriptProcedure);
+ LockNotPinnedObject(TypeRelationId, baseType);
+ LockNotPinnedObject(CollationRelationId, typeCollation);
+ LockNotPinnedObject(TypeRelationId, elementType);
+
+ /*
+ * No need to call LockRelationOid() (through LockNotPinnedObject())
+ * on relationOid as relationOid is set to an InvalidOid or to a new
+ * Oid not added to pg_class yet (In heap_create_with_catalog(),
+ * AddNewRelationType() is called before AddNewRelationTuple()).
+ */
+ if (relationKind == RELKIND_COMPOSITE_TYPE)
+ LockNotPinnedObject(TypeRelationId, typeObjectId);
+
GenerateTypeDependencies(tup,
pg_type_desc,
(defaultTypeBin ?
@@ -505,6 +543,7 @@ TypeCreate(Oid newTypeOid,
isDependentType,
true, /* make extension dependency */
rebuildDeps);
+ }
/* Post creation hook for new type */
InvokeObjectPostCreateHook(TypeRelationId, typeObjectId, 0);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 738bc46ae8..a4d8342ca1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -367,6 +367,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
toastobject.objectId = toast_relid;
toastobject.objectSubId = 0;
+ LockNotPinnedObject(RelationRelationId, relOid);
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
}
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f99ebb447..57e86f576a 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -501,7 +501,10 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre
currexts = getAutoExtensionsOfObject(address.classId,
address.objectId);
if (!list_member_oid(currexts, refAddr.objectId))
+ {
+ LockNotPinnedObject(refAddr.classId, refAddr.objectId);
recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
+ }
}
return address;
@@ -807,6 +810,7 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
pfree(replaces);
/* update dependency to point to the new schema */
+ LockNotPinnedObject(NamespaceRelationId, nspOid);
if (changeDependencyFor(classId, objid,
NamespaceRelationId, oldNspOid, nspOid) != 1)
elog(ERROR, "could not change schema dependency for object %u",
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index aaa0f9a1dc..8616a7c9fa 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -104,6 +104,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
referenced.objectId = amhandler;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ProcedureRelationId, amhandler);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
recordDependencyOnCurrentExtension(&myself, false);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 78f96789b0..fb95d17738 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1272,6 +1272,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
*/
if (relam1 != relam2)
{
+ LockNotPinnedObject(AccessMethodRelationId, relam2);
if (changeDependencyFor(RelationRelationId,
r1,
AccessMethodRelationId,
@@ -1280,6 +1281,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
elog(ERROR, "could not change access method dependency for relation \"%s.%s\"",
get_namespace_name(get_rel_namespace(r1)),
get_rel_name(r1));
+
+ LockNotPinnedObject(AccessMethodRelationId, relam1);
if (changeDependencyFor(RelationRelationId,
r2,
AccessMethodRelationId,
@@ -1381,6 +1384,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
{
baseobject.objectId = r1;
toastobject.objectId = relform1->reltoastrelid;
+
+ LockNotPinnedObject(RelationRelationId, r1);
recordDependencyOn(&toastobject, &baseobject,
DEPENDENCY_INTERNAL);
}
@@ -1389,6 +1394,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
{
baseobject.objectId = r2;
toastobject.objectId = relform2->reltoastrelid;
+
+ LockNotPinnedObject(RelationRelationId, r2);
recordDependencyOn(&toastobject, &baseobject,
DEPENDENCY_INTERNAL);
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 7a5ed6b985..8d0cdec59e 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -327,6 +327,7 @@ insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtO
referenced.classId = ProcedureRelationId;
referenced.objectId = funcoid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ProcedureRelationId, funcoid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* Depend on extension, if any. */
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 1643c8c69a..669a5d6dd8 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1924,6 +1924,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
add_exact_object_address(&nsp, refobjs);
+ LockNotPinnedObject(NamespaceRelationId, schemaOid);
foreach(lc, requiredExtensions)
{
@@ -1932,6 +1933,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
ObjectAddressSet(otherext, ExtensionRelationId, reqext);
add_exact_object_address(&otherext, refobjs);
+ LockNotPinnedObject(ExtensionRelationId, reqext);
}
/* Record all of them (this includes duplicate elimination) */
@@ -2968,6 +2970,7 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
table_close(extRel, RowExclusiveLock);
/* update dependency to point to the new schema */
+ LockNotPinnedObject(NamespaceRelationId, nspOid);
if (changeDependencyFor(ExtensionRelationId, extensionOid,
NamespaceRelationId, oldNspOid, nspOid) != 1)
elog(ERROR, "could not change schema dependency for extension %s",
@@ -3258,6 +3261,7 @@ ApplyExtensionUpdates(Oid extensionOid,
otherext.objectId = reqext;
otherext.objectSubId = 0;
+ LockNotPinnedObject(ExtensionRelationId, reqext);
recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
}
@@ -3414,6 +3418,7 @@ ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt,
/*
* OK, add the dependency.
*/
+ LockNotPinnedObject(extension.classId, extension.objectId);
recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index cf61bbac1f..735bca486c 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -642,6 +642,7 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
referenced.classId = ProcedureRelationId;
referenced.objectId = fdwhandler;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ProcedureRelationId, fdwhandler);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
@@ -650,6 +651,7 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
referenced.classId = ProcedureRelationId;
referenced.objectId = fdwvalidator;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ProcedureRelationId, fdwvalidator);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
@@ -811,6 +813,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
referenced.classId = ProcedureRelationId;
referenced.objectId = fdwhandler;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ProcedureRelationId, fdwhandler);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
@@ -819,6 +822,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
referenced.classId = ProcedureRelationId;
referenced.objectId = fdwvalidator;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ProcedureRelationId, fdwvalidator);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
}
@@ -951,6 +955,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
referenced.classId = ForeignDataWrapperRelationId;
referenced.objectId = fdw->fdwid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ForeignDataWrapperRelationId, fdw->fdwid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
@@ -1195,6 +1200,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
referenced.classId = ForeignServerRelationId;
referenced.objectId = srv->serverid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ForeignServerRelationId, srv->serverid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
if (OidIsValid(useId))
@@ -1472,6 +1478,7 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
referenced.classId = ForeignServerRelationId;
referenced.objectId = server->serverid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ForeignServerRelationId, server->serverid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
table_close(ftrel, RowExclusiveLock);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 6593fd7d81..8207ef08b3 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1446,6 +1446,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
/* Add or replace dependency on support function */
if (OidIsValid(procForm->prosupport))
{
+ LockNotPinnedObject(ProcedureRelationId, newsupport);
if (changeDependencyFor(ProcedureRelationId, funcOid,
ProcedureRelationId, procForm->prosupport,
newsupport) != 1)
@@ -1459,6 +1460,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
referenced.classId = ProcedureRelationId;
referenced.objectId = newsupport;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ProcedureRelationId, newsupport);
recordDependencyOn(&address, &referenced, DEPENDENCY_NORMAL);
}
@@ -1962,21 +1964,25 @@ CreateTransform(CreateTransformStmt *stmt)
/* dependency on language */
ObjectAddressSet(referenced, LanguageRelationId, langid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(LanguageRelationId, langid);
/* dependency on type */
ObjectAddressSet(referenced, TypeRelationId, typeid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(TypeRelationId, typeid);
/* dependencies on functions */
if (OidIsValid(fromsqlfuncid))
{
ObjectAddressSet(referenced, ProcedureRelationId, fromsqlfuncid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, fromsqlfuncid);
}
if (OidIsValid(tosqlfuncid))
{
ObjectAddressSet(referenced, ProcedureRelationId, tosqlfuncid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, tosqlfuncid);
}
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 2caab88aa5..e6ff8476e8 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -4380,8 +4380,10 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
ObjectAddressSet(partitionTbl, RelationRelationId,
partitionIdx->rd_index->indrelid);
+ LockNotPinnedObject(RelationRelationId, parentOid);
recordDependencyOn(&partIdx, &parentIdx,
DEPENDENCY_PARTITION_PRI);
+ LockNotPinnedObject(RelationRelationId, partitionIdx->rd_index->indrelid);
recordDependencyOn(&partIdx, &partitionTbl,
DEPENDENCY_PARTITION_SEC);
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index b8b5c147c5..e70afd216c 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -298,12 +298,14 @@ CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname,
referenced.classId = AccessMethodRelationId;
referenced.objectId = amoid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(AccessMethodRelationId, amoid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
/* dependency on namespace */
referenced.classId = NamespaceRelationId;
referenced.objectId = namespaceoid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(NamespaceRelationId, namespaceoid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* dependency on owner */
@@ -725,18 +727,21 @@ DefineOpClass(CreateOpClassStmt *stmt)
referenced.classId = NamespaceRelationId;
referenced.objectId = namespaceoid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(NamespaceRelationId, namespaceoid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* dependency on opfamily */
referenced.classId = OperatorFamilyRelationId;
referenced.objectId = opfamilyoid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(OperatorFamilyRelationId, opfamilyoid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
/* dependency on indexed datatype */
referenced.classId = TypeRelationId;
referenced.objectId = typeoid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(TypeRelationId, typeoid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* dependency on storage datatype */
@@ -745,6 +750,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
referenced.classId = TypeRelationId;
referenced.objectId = storageoid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(TypeRelationId, storageoid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
@@ -1486,6 +1492,13 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
heap_freetuple(tup);
+ /*
+ * CommandCounterIncrement() here to ensure the new operator entry is
+ * visible when LockNotPinnedObject() will check its existence before
+ * recording the dependencies.
+ */
+ CommandCounterIncrement();
+
/* Make its dependencies */
myself.classId = AccessMethodOperatorRelationId;
myself.objectId = entryoid;
@@ -1496,6 +1509,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
referenced.objectSubId = 0;
/* see comments in amapi.h about dependency strength */
+ LockNotPinnedObject(OperatorRelationId, op->object);
recordDependencyOn(&myself, &referenced,
op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
@@ -1504,6 +1518,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
referenced.objectId = op->refobjid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(referenced.classId, op->refobjid);
recordDependencyOn(&myself, &referenced,
op->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
@@ -1514,6 +1529,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
referenced.objectId = op->sortfamily;
referenced.objectSubId = 0;
+ LockNotPinnedObject(OperatorFamilyRelationId, op->sortfamily);
recordDependencyOn(&myself, &referenced,
op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
}
@@ -1597,6 +1613,7 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
referenced.objectSubId = 0;
/* see comments in amapi.h about dependency strength */
+ LockNotPinnedObject(ProcedureRelationId, proc->object);
recordDependencyOn(&myself, &referenced,
proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
@@ -1605,6 +1622,7 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
referenced.objectId = proc->refobjid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(referenced.classId, proc->refobjid);
recordDependencyOn(&myself, &referenced,
proc->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 5872a3e192..58a69e7cc2 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -33,6 +33,7 @@
#include "access/htup_details.h"
#include "access/table.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
@@ -656,11 +657,15 @@ AlterOperator(AlterOperatorStmt *stmt)
{
replaces[Anum_pg_operator_oprrest - 1] = true;
values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
+ if (OidIsValid(restrictionOid))
+ LockNotPinnedObject(ProcedureRelationId, restrictionOid);
}
if (updateJoin)
{
replaces[Anum_pg_operator_oprjoin - 1] = true;
values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
+ if (OidIsValid(joinOid))
+ LockNotPinnedObject(ProcedureRelationId, joinOid);
}
if (OidIsValid(commutatorOid))
{
@@ -688,6 +693,31 @@ AlterOperator(AlterOperatorStmt *stmt)
CatalogTupleUpdate(catalog, &tup->t_self, tup);
+
+ /* Lock dependent objects */
+ oprForm = (Form_pg_operator) GETSTRUCT(tup);
+
+ if (OidIsValid(oprForm->oprnamespace))
+ LockNotPinnedObject(NamespaceRelationId, oprForm->oprnamespace);
+
+ if (OidIsValid(oprForm->oprleft))
+ LockNotPinnedObject(TypeRelationId, oprForm->oprleft);
+
+ if (OidIsValid(oprForm->oprright))
+ LockNotPinnedObject(TypeRelationId, oprForm->oprright);
+
+ if (OidIsValid(oprForm->oprresult))
+ LockNotPinnedObject(TypeRelationId, oprForm->oprresult);
+
+ if (OidIsValid(oprForm->oprcode))
+ LockNotPinnedObject(ProcedureRelationId, oprForm->oprcode);
+
+ if (OidIsValid(oprForm->oprrest))
+ LockNotPinnedObject(ProcedureRelationId, oprForm->oprrest);
+
+ if (OidIsValid(oprForm->oprjoin))
+ LockNotPinnedObject(ProcedureRelationId, oprForm->oprjoin);
+
address = makeOperatorDependencies(tup, false, true);
if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 6ff3eba824..9da98cbeec 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -722,6 +722,7 @@ CreatePolicy(CreatePolicyStmt *stmt)
myself.objectId = policy_id;
myself.objectSubId = 0;
+ LockNotPinnedObject(RelationRelationId, table_id);
recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
@@ -1053,6 +1054,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
myself.objectId = policy_id;
myself.objectSubId = 0;
+ LockNotPinnedObject(RelationRelationId, table_id);
recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 881f90017e..fadfd9064f 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -190,12 +190,14 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
/* dependency on the PL handler function */
ObjectAddressSet(referenced, ProcedureRelationId, handlerOid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, handlerOid);
/* dependency on the inline handler function, if any */
if (OidIsValid(inlineOid))
{
ObjectAddressSet(referenced, ProcedureRelationId, inlineOid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, inlineOid);
}
/* dependency on the validator function, if any */
@@ -203,6 +205,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
{
ObjectAddressSet(referenced, ProcedureRelationId, valOid);
add_exact_object_address(&referenced, addrs);
+ LockNotPinnedObject(ProcedureRelationId, valOid);
}
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 9f28d40466..c0634d0af9 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1688,6 +1688,8 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
depobject.classId = RelationRelationId;
depobject.objectId = RelationGetRelid(seqrel);
depobject.objectSubId = 0;
+
+ LockNotPinnedObject(RelationRelationId, RelationGetRelid(tablerel));
recordDependencyOn(&depobject, &refobject, deptype);
}
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 1db3ef69d2..9f0b03388a 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -88,6 +88,7 @@ CreateStatistics(CreateStatsStmt *stmt)
bool build_mcv;
bool build_expressions;
bool requested_type = false;
+ bool locked_object = false;
int i;
ListCell *cell;
ListCell *cell2;
@@ -536,6 +537,12 @@ CreateStatistics(CreateStatsStmt *stmt)
for (i = 0; i < nattnums; i++)
{
ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
+
+ if (!locked_object)
+ {
+ LockNotPinnedObject(RelationRelationId, relid);
+ locked_object = true;
+ }
recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
}
@@ -553,6 +560,8 @@ CreateStatistics(CreateStatsStmt *stmt)
if (!nattnums)
{
ObjectAddressSet(parentobject, RelationRelationId, relid);
+
+ LockNotPinnedObject(RelationRelationId, relid);
recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
}
@@ -573,6 +582,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* than the underlying table(s).
*/
ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId);
+ LockNotPinnedObject(NamespaceRelationId, namespaceId);
recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL);
recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index dbfe0d6b1c..fc7ddf0968 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3419,6 +3419,7 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid,
childobject.objectId = relationId;
childobject.objectSubId = 0;
+ LockNotPinnedObject(RelationRelationId, parentOid);
recordDependencyOn(&childobject, &parentobject,
child_dependency_type(child_is_partition));
@@ -7342,7 +7343,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
/*
* Add needed dependency entries for the new column.
*/
+ LockNotPinnedObject(TypeRelationId, attribute->atttypid);
add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
+ LockNotPinnedObject(CollationRelationId, attribute->attcollation);
add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
/*
@@ -10167,6 +10170,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
ObjectAddress referenced;
ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
+ LockNotPinnedObject(ConstraintRelationId, parentConstr);
recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
}
@@ -10458,8 +10462,11 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
*/
ObjectAddressSet(address, ConstraintRelationId, constrOid);
ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
+ LockNotPinnedObject(ConstraintRelationId, parentConstr);
recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
ObjectAddressSet(referenced, RelationRelationId, partitionId);
+
+ LockNotPinnedObject(RelationRelationId, partitionId);
recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
/* Make all this visible before recursing */
@@ -10960,9 +10967,12 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
/* Set up partition dependencies for the new constraint */
ObjectAddressSet(address, ConstraintRelationId, constrOid);
ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
+ LockDatabaseObject(ConstraintRelationId, parentConstrOid, 0, AccessShareLock);
recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
ObjectAddressSet(referenced, RelationRelationId,
RelationGetRelid(partRel));
+
+ LockNotPinnedObject(RelationRelationId, RelationGetRelid(partRel));
recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
/* Done with the cloned constraint's tuple */
@@ -13248,7 +13258,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
table_close(attrelation, RowExclusiveLock);
/* Install dependencies on new datatype and collation */
+ LockNotPinnedObject(TypeRelationId, targettype);
add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
+ LockNotPinnedObject(CollationRelationId, targetcollid);
add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
/*
@@ -14810,6 +14822,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
*/
ObjectAddressSet(relobj, RelationRelationId, reloid);
ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+ LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
}
else if (OidIsValid(oldAccessMethodId) &&
@@ -14829,6 +14842,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
OidIsValid(rd_rel->relam));
/* Both are valid, so update the dependency */
+ LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
changeDependencyFor(RelationRelationId, reloid,
AccessMethodRelationId,
oldAccessMethodId, rd_rel->relam);
@@ -16428,6 +16442,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
typeobj.classId = TypeRelationId;
typeobj.objectId = typeid;
typeobj.objectSubId = 0;
+ LockNotPinnedObject(TypeRelationId, typeid);
recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
/* Update pg_class.reloftype */
@@ -17186,14 +17201,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
/* Update dependency on schema if caller said so */
- if (hasDependEntry &&
- changeDependencyFor(RelationRelationId,
- relOid,
- NamespaceRelationId,
- oldNspOid,
- newNspOid) != 1)
- elog(ERROR, "could not change schema dependency for relation \"%s\"",
- NameStr(classForm->relname));
+ if (hasDependEntry)
+ {
+ LockNotPinnedObject(NamespaceRelationId, newNspOid);
+ if (changeDependencyFor(RelationRelationId,
+ relOid,
+ NamespaceRelationId,
+ oldNspOid,
+ newNspOid) != 1)
+ elog(ERROR, "could not change schema dependency for relation \"%s\"",
+ NameStr(classForm->relname));
+ }
}
if (!already_done)
{
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 170360edda..1250673b16 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1018,8 +1018,6 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
-
- CommandCounterIncrement();
}
else
CacheInvalidateRelcacheByTuple(tuple);
@@ -1027,6 +1025,13 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
heap_freetuple(tuple);
table_close(pgrel, RowExclusiveLock);
+ /*
+ * CommandCounterIncrement() here to ensure the new trigger entry is
+ * visible when LockNotPinnedObject() will check its existence before
+ * recording the dependencies.
+ */
+ CommandCounterIncrement();
+
/*
* If we're replacing a trigger, flush all the old dependencies before
* recording new ones.
@@ -1045,6 +1050,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
referenced.classId = ProcedureRelationId;
referenced.objectId = funcoid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ProcedureRelationId, funcoid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
if (isInternal && OidIsValid(constraintOid))
@@ -1058,6 +1064,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
referenced.classId = ConstraintRelationId;
referenced.objectId = constraintOid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(ConstraintRelationId, constraintOid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
}
else
@@ -1070,6 +1077,8 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
referenced.classId = RelationRelationId;
referenced.objectId = RelationGetRelid(rel);
referenced.objectSubId = 0;
+
+ LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
if (OidIsValid(constrrelid))
@@ -1077,6 +1086,8 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
referenced.classId = RelationRelationId;
referenced.objectId = constrrelid;
referenced.objectSubId = 0;
+
+ LockNotPinnedObject(RelationRelationId, constrrelid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
/* Not possible to have an index dependency in this case */
@@ -1091,6 +1102,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
referenced.classId = ConstraintRelationId;
referenced.objectId = constraintOid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(TriggerRelationId, trigoid);
recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
}
@@ -1100,8 +1112,11 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
if (OidIsValid(parentTriggerOid))
{
ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
+ LockNotPinnedObject(TriggerRelationId, parentTriggerOid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
+
+ LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
}
}
@@ -1110,12 +1125,19 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
if (columns != NULL)
{
int i;
+ bool locked_object = false;
referenced.classId = RelationRelationId;
referenced.objectId = RelationGetRelid(rel);
for (i = 0; i < ncolumns; i++)
{
referenced.objectSubId = columns[i];
+
+ if (!locked_object)
+ {
+ LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
+ locked_object = true;
+ }
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
}
@@ -1255,9 +1277,12 @@ TriggerSetParentTrigger(Relation trigRel,
ObjectAddressSet(depender, TriggerRelationId, childTrigId);
ObjectAddressSet(referenced, TriggerRelationId, parentTrigId);
+ LockNotPinnedObject(TriggerRelationId, parentTrigId);
recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
ObjectAddressSet(referenced, RelationRelationId, childTableId);
+
+ LockNotPinnedObject(RelationRelationId, childTableId);
recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
}
else
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index b7b5019f1e..1b90e187ea 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -214,6 +214,7 @@ DefineTSParser(List *names, List *parameters)
namestrcpy(&pname, prsname);
values[Anum_pg_ts_parser_prsname - 1] = NameGetDatum(&pname);
values[Anum_pg_ts_parser_prsnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+ LockNotPinnedObject(NamespaceRelationId, namespaceoid);
/*
* loop over the definition list and extract the information we need.
@@ -224,28 +225,48 @@ DefineTSParser(List *names, List *parameters)
if (strcmp(defel->defname, "start") == 0)
{
+ Oid procoid;
+
values[Anum_pg_ts_parser_prsstart - 1] =
get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart);
+ procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]);
+ LockNotPinnedObject(ProcedureRelationId, procoid);
}
else if (strcmp(defel->defname, "gettoken") == 0)
{
+ Oid procoid;
+
values[Anum_pg_ts_parser_prstoken - 1] =
get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken);
+ procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]);
+ LockNotPinnedObject(ProcedureRelationId, procoid);
}
else if (strcmp(defel->defname, "end") == 0)
{
+ Oid procoid;
+
values[Anum_pg_ts_parser_prsend - 1] =
get_ts_parser_func(defel, Anum_pg_ts_parser_prsend);
+ procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]);
+ LockNotPinnedObject(ProcedureRelationId, procoid);
}
else if (strcmp(defel->defname, "headline") == 0)
{
+ Oid procoid;
+
values[Anum_pg_ts_parser_prsheadline - 1] =
get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline);
+ procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]);
+ LockNotPinnedObject(ProcedureRelationId, procoid);
}
else if (strcmp(defel->defname, "lextypes") == 0)
{
+ Oid procoid;
+
values[Anum_pg_ts_parser_prslextype - 1] =
get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype);
+ procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1]);
+ LockNotPinnedObject(ProcedureRelationId, procoid);
}
else
ereport(ERROR,
@@ -474,6 +495,10 @@ DefineTSDictionary(List *names, List *parameters)
CatalogTupleInsert(dictRel, tup);
+ /* Lock dependent objects */
+ LockNotPinnedObject(NamespaceRelationId, namespaceoid);
+ LockNotPinnedObject(TSTemplateRelationId, templId);
+
address = makeDictionaryDependencies(tup);
/* Post creation hook for new text search dictionary */
@@ -723,6 +748,7 @@ DefineTSTemplate(List *names, List *parameters)
namestrcpy(&dname, tmplname);
values[Anum_pg_ts_template_tmplname - 1] = NameGetDatum(&dname);
values[Anum_pg_ts_template_tmplnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+ LockNotPinnedObject(NamespaceRelationId, namespaceoid);
/*
* loop over the definition list and extract the information we need.
@@ -733,15 +759,23 @@ DefineTSTemplate(List *names, List *parameters)
if (strcmp(defel->defname, "init") == 0)
{
+ Oid procoid;
+
values[Anum_pg_ts_template_tmplinit - 1] =
get_ts_template_func(defel, Anum_pg_ts_template_tmplinit);
nulls[Anum_pg_ts_template_tmplinit - 1] = false;
+ procoid = DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]);
+ LockNotPinnedObject(ProcedureRelationId, procoid);
}
else if (strcmp(defel->defname, "lexize") == 0)
{
+ Oid procoid;
+
values[Anum_pg_ts_template_tmpllexize - 1] =
get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize);
nulls[Anum_pg_ts_template_tmpllexize - 1] = false;
+ procoid = DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1]);
+ LockNotPinnedObject(ProcedureRelationId, procoid);
}
else
ereport(ERROR,
@@ -998,6 +1032,10 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId());
values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);
+ /* Lock dependent objects */
+ LockNotPinnedObject(NamespaceRelationId, namespaceoid);
+ LockNotPinnedObject(TSParserRelationId, prsOid);
+
tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
CatalogTupleInsert(cfgRel, tup);
@@ -1063,6 +1101,7 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
+ LockNotPinnedObject(TSDictionaryRelationId, cfgmap->mapdict);
ExecStoreVirtualTuple(slot[slot_stored_count]);
slot_stored_count++;
@@ -1156,9 +1195,13 @@ ObjectAddress
AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
{
HeapTuple tup;
+ Form_pg_ts_config cfg;
Oid cfgId;
Relation relMap;
ObjectAddress address;
+ ScanKeyData skey;
+ SysScanDesc scan;
+ HeapTuple maptup;
/* Find the configuration */
tup = GetTSConfigTuple(stmt->cfgname);
@@ -1168,7 +1211,8 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
errmsg("text search configuration \"%s\" does not exist",
NameListToString(stmt->cfgname))));
- cfgId = ((Form_pg_ts_config) GETSTRUCT(tup))->oid;
+ cfg = (Form_pg_ts_config) GETSTRUCT(tup);
+ cfgId = cfg->oid;
/* must be owner */
if (!object_ownercheck(TSConfigRelationId, cfgId, GetUserId()))
@@ -1183,6 +1227,28 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
else if (stmt->tokentype)
DropConfigurationMapping(stmt, tup, relMap);
+ /* Lock dependent objects */
+
+ ScanKeyInit(&skey,
+ Anum_pg_ts_config_map_mapcfg,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(cfgId));
+
+ scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
+ NULL, 1, &skey);
+
+ while (HeapTupleIsValid((maptup = systable_getnext(scan))))
+ {
+ Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
+
+ LockNotPinnedObject(TSDictionaryRelationId, cfgmap->mapdict);
+ }
+
+ systable_endscan(scan);
+
+ LockNotPinnedObject(NamespaceRelationId, cfg->cfgnamespace);
+ LockNotPinnedObject(TSParserRelationId, cfg->cfgparser);
+
/* Update dependencies */
makeConfigurationDependencies(tup, true, relMap);
@@ -1414,6 +1480,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
+ LockNotPinnedObject(TSDictionaryRelationId, dictNew);
+
newtup = heap_modify_tuple(maptup,
RelationGetDescr(relMap),
repl_val, repl_null, repl_repl);
@@ -1456,6 +1524,9 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
+ /* Lock dependent objects */
+ LockNotPinnedObject(TSDictionaryRelationId, dictIds[j]);
+
ExecStoreVirtualTuple(slot[slotCount]);
slotCount++;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 2a1e713335..9febaa24a7 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1794,6 +1794,7 @@ makeRangeConstructors(const char *name, Oid namespace,
* that they go away silently when the type is dropped. Note that
* pg_dump depends on this choice to avoid dumping the constructors.
*/
+ LockNotPinnedObject(TypeRelationId, rangeOid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
}
}
@@ -1859,6 +1860,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
* that they go away silently when the type is dropped. Note that pg_dump
* depends on this choice to avoid dumping the constructors.
*/
+ LockNotPinnedObject(TypeRelationId, multirangeOid);
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
pfree(argtypes);
@@ -2672,6 +2674,45 @@ AlterDomainDefault(List *names, Node *defaultRaw)
CatalogTupleUpdate(rel, &tup->t_self, newtuple);
+ /* Lock dependent objects */
+ typTup = (Form_pg_type) GETSTRUCT(newtuple);
+
+ if (OidIsValid(typTup->typnamespace))
+ LockNotPinnedObject(NamespaceRelationId, typTup->typnamespace);
+
+ if (OidIsValid(typTup->typinput))
+ LockNotPinnedObject(ProcedureRelationId, typTup->typinput);
+
+ if (OidIsValid(typTup->typoutput))
+ LockNotPinnedObject(ProcedureRelationId, typTup->typoutput);
+
+ if (OidIsValid(typTup->typreceive))
+ LockNotPinnedObject(ProcedureRelationId, typTup->typreceive);
+
+ if (OidIsValid(typTup->typsend))
+ LockNotPinnedObject(ProcedureRelationId, typTup->typsend);
+
+ if (OidIsValid(typTup->typmodin))
+ LockNotPinnedObject(ProcedureRelationId, typTup->typmodin);
+
+ if (OidIsValid(typTup->typmodout))
+ LockNotPinnedObject(ProcedureRelationId, typTup->typmodout);
+
+ if (OidIsValid(typTup->typanalyze))
+ LockNotPinnedObject(ProcedureRelationId, typTup->typanalyze);
+
+ if (OidIsValid(typTup->typsubscript))
+ LockNotPinnedObject(ProcedureRelationId, typTup->typsubscript);
+
+ if (OidIsValid(typTup->typbasetype))
+ LockNotPinnedObject(TypeRelationId, typTup->typbasetype);
+
+ if (OidIsValid(typTup->typcollation))
+ LockNotPinnedObject(CollationRelationId, typTup->typcollation);
+
+ if (OidIsValid(typTup->typelem))
+ LockNotPinnedObject(TypeRelationId, typTup->typelem);
+
/* Rebuild dependencies */
GenerateTypeDependencies(newtuple,
rel,
@@ -4276,10 +4317,13 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
if (oldNspOid != nspOid &&
(isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
!isImplicitArray)
+ {
+ LockNotPinnedObject(NamespaceRelationId, nspOid);
if (changeDependencyFor(TypeRelationId, typeOid,
NamespaceRelationId, oldNspOid, nspOid) != 1)
elog(ERROR, "could not change schema dependency for type \"%s\"",
format_type_be(typeOid));
+ }
InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
@@ -4571,6 +4615,7 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
SysScanDesc scan;
ScanKeyData key[1];
HeapTuple domainTup;
+ Form_pg_type typeForm;
/* Since this function recurses, it could be driven to stack overflow */
check_stack_depth();
@@ -4619,6 +4664,45 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
newtup = heap_modify_tuple(tup, RelationGetDescr(catalog),
values, nulls, replaces);
+ /* Lock dependent objects */
+ typeForm = (Form_pg_type) GETSTRUCT(newtup);
+
+ if (OidIsValid(typeForm->typnamespace))
+ LockNotPinnedObject(NamespaceRelationId, typeForm->typnamespace);
+
+ if (OidIsValid(typeForm->typinput))
+ LockNotPinnedObject(ProcedureRelationId, typeForm->typinput);
+
+ if (OidIsValid(typeForm->typoutput))
+ LockNotPinnedObject(ProcedureRelationId, typeForm->typoutput);
+
+ if (OidIsValid(typeForm->typreceive))
+ LockNotPinnedObject(ProcedureRelationId, typeForm->typreceive);
+
+ if (OidIsValid(typeForm->typsend))
+ LockNotPinnedObject(ProcedureRelationId, typeForm->typsend);
+
+ if (OidIsValid(typeForm->typmodin))
+ LockNotPinnedObject(ProcedureRelationId, typeForm->typmodin);
+
+ if (OidIsValid(typeForm->typmodout))
+ LockNotPinnedObject(ProcedureRelationId, typeForm->typmodout);
+
+ if (OidIsValid(typeForm->typanalyze))
+ LockNotPinnedObject(ProcedureRelationId, typeForm->typanalyze);
+
+ if (OidIsValid(typeForm->typsubscript))
+ LockNotPinnedObject(ProcedureRelationId, typeForm->typsubscript);
+
+ if (OidIsValid(typeForm->typbasetype))
+ LockNotPinnedObject(TypeRelationId, typeForm->typbasetype);
+
+ if (OidIsValid(typeForm->typcollation))
+ LockNotPinnedObject(CollationRelationId, typeForm->typcollation);
+
+ if (OidIsValid(typeForm->typelem))
+ LockNotPinnedObject(TypeRelationId, typeForm->typelem);
+
CatalogTupleUpdate(catalog, &newtup->t_self, newtup);
/* Rebuild dependencies for this type */
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 6cc9a8d8bf..c930eca262 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -155,6 +155,7 @@ InsertRule(const char *rulname,
referenced.objectId = eventrel_oid;
referenced.objectSubId = 0;
+ LockNotPinnedObject(RelationRelationId, eventrel_oid);
recordDependencyOn(&myself, &referenced,
(evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index 3250d539e1..60e8539fe3 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -271,6 +271,7 @@ Section: Class 28 - Invalid Authorization Specification
Section: Class 2B - Dependent Privilege Descriptors Still Exist
2B000 E ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST dependent_privilege_descriptors_still_exist
+2BP02 E ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST dependent_objects_does_not_exist
2BP01 E ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST dependent_objects_still_exist
Section: Class 2D - Invalid Transaction Termination
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 6908ca7180..0546bcbe16 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -101,6 +101,8 @@ typedef struct ObjectAddresses ObjectAddresses;
/* in dependency.c */
extern void AcquireDeletionLock(const ObjectAddress *object, int flags);
+extern void LockNotPinnedObjectById(const ObjectAddress *object);
+extern void LockNotPinnedObject(Oid classid, Oid objid);
extern void ReleaseDeletionLock(const ObjectAddress *object);
@@ -172,6 +174,7 @@ extern long changeDependenciesOf(Oid classId, Oid oldObjectId,
extern long changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
Oid newRefObjectId);
+extern bool isObjectPinned(const ObjectAddress *object);
extern Oid getExtensionOfObject(Oid classId, Oid objectId);
extern List *getAutoExtensionsOfObject(Oid classId, Oid objectId);
diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h
index 3a70d80e32..56f746264b 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -53,6 +53,7 @@ extern void check_object_ownership(Oid roleid,
Node *object, Relation relation);
extern Oid get_object_namespace(const ObjectAddress *address);
+extern bool ObjectByIdExist(const ObjectAddress *address);
extern bool is_objectclass_supported(Oid class_id);
extern const char *get_object_class_descr(Oid class_id);
diff --git a/src/test/isolation/expected/test_dependencies_locks.out b/src/test/isolation/expected/test_dependencies_locks.out
new file mode 100644
index 0000000000..9b645d7aa5
--- /dev/null
+++ b/src/test/isolation/expected/test_dependencies_locks.out
@@ -0,0 +1,129 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_create_function_in_schema s2_drop_schema s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+step s2_drop_schema: DROP SCHEMA testschema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_schema: <... completed>
+ERROR: cannot drop schema testschema because other objects depend on it
+
+starting permutation: s2_begin s2_drop_schema s1_create_function_in_schema s2_commit
+step s2_begin: BEGIN;
+step s2_drop_schema: DROP SCHEMA testschema;
+step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_in_schema: <... completed>
+ERROR: schema testschema does not exist
+
+starting permutation: s1_begin s1_alter_function_schema s2_drop_alterschema s1_commit
+step s1_begin: BEGIN;
+step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema;
+step s2_drop_alterschema: DROP SCHEMA alterschema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_alterschema: <... completed>
+ERROR: cannot drop schema alterschema because other objects depend on it
+
+starting permutation: s2_begin s2_drop_alterschema s1_alter_function_schema s2_commit
+step s2_begin: BEGIN;
+step s2_drop_alterschema: DROP SCHEMA alterschema;
+step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema; <waiting ...>
+step s2_commit: COMMIT;
+step s1_alter_function_schema: <... completed>
+ERROR: schema alterschema does not exist
+
+starting permutation: s1_begin s1_create_function_with_argtype s2_drop_foo_type s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql;
+step s2_drop_foo_type: DROP TYPE public.foo; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_foo_type: <... completed>
+ERROR: cannot drop type foo because other objects depend on it
+
+starting permutation: s2_begin s2_drop_foo_type s1_create_function_with_argtype s2_commit
+step s2_begin: BEGIN;
+step s2_drop_foo_type: DROP TYPE public.foo;
+step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_argtype: <... completed>
+ERROR: type foo does not exist
+
+starting permutation: s1_begin s1_create_function_with_rettype s2_drop_foo_rettype s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1;
+step s2_drop_foo_rettype: DROP DOMAIN id; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_foo_rettype: <... completed>
+ERROR: cannot drop type id because other objects depend on it
+
+starting permutation: s2_begin s2_drop_foo_rettype s1_create_function_with_rettype s2_commit
+step s2_begin: BEGIN;
+step s2_drop_foo_rettype: DROP DOMAIN id;
+step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_rettype: <... completed>
+ERROR: type id does not exist
+
+starting permutation: s1_begin s1_create_function_with_function s2_drop_function_f s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1;
+step s2_drop_function_f: DROP FUNCTION f(); <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_function_f: <... completed>
+ERROR: cannot drop function f() because other objects depend on it
+
+starting permutation: s2_begin s2_drop_function_f s1_create_function_with_function s2_commit
+step s2_begin: BEGIN;
+step s2_drop_function_f: DROP FUNCTION f();
+step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_function: <... completed>
+ERROR: function f() does not exist
+
+starting permutation: s1_begin s1_create_domain_with_domain s2_drop_domain_id s1_commit
+step s1_begin: BEGIN;
+step s1_create_domain_with_domain: CREATE DOMAIN idid as id;
+step s2_drop_domain_id: DROP DOMAIN id; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_domain_id: <... completed>
+ERROR: cannot drop type id because other objects depend on it
+
+starting permutation: s2_begin s2_drop_domain_id s1_create_domain_with_domain s2_commit
+step s2_begin: BEGIN;
+step s2_drop_domain_id: DROP DOMAIN id;
+step s1_create_domain_with_domain: CREATE DOMAIN idid as id; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_domain_with_domain: <... completed>
+ERROR: type id does not exist
+
+starting permutation: s1_begin s1_create_table_with_type s2_drop_footab_type s1_commit
+step s1_begin: BEGIN;
+step s1_create_table_with_type: CREATE TABLE tabtype(a footab);
+step s2_drop_footab_type: DROP TYPE public.footab; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_footab_type: <... completed>
+ERROR: cannot drop type footab because other objects depend on it
+
+starting permutation: s2_begin s2_drop_footab_type s1_create_table_with_type s2_commit
+step s2_begin: BEGIN;
+step s2_drop_footab_type: DROP TYPE public.footab;
+step s1_create_table_with_type: CREATE TABLE tabtype(a footab); <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_table_with_type: <... completed>
+ERROR: type footab does not exist
+
+starting permutation: s1_begin s1_create_server_with_fdw_wrapper s2_drop_fdw_wrapper s1_commit
+step s1_begin: BEGIN;
+step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper;
+step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_fdw_wrapper: <... completed>
+ERROR: cannot drop foreign-data wrapper fdw_wrapper because other objects depend on it
+
+starting permutation: s2_begin s2_drop_fdw_wrapper s1_create_server_with_fdw_wrapper s2_commit
+step s2_begin: BEGIN;
+step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT;
+step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_server_with_fdw_wrapper: <... completed>
+ERROR: foreign-data wrapper fdw_wrapper does not exist
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 6da98cffac..ef6a7075bc 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -117,3 +117,4 @@ test: serializable-parallel-2
test: serializable-parallel-3
test: matview-write-skew
test: lock-nowait
+test: test_dependencies_locks
diff --git a/src/test/isolation/specs/test_dependencies_locks.spec b/src/test/isolation/specs/test_dependencies_locks.spec
new file mode 100644
index 0000000000..5d04dfe9dc
--- /dev/null
+++ b/src/test/isolation/specs/test_dependencies_locks.spec
@@ -0,0 +1,89 @@
+setup
+{
+ CREATE SCHEMA testschema;
+ CREATE SCHEMA alterschema;
+ CREATE TYPE public.foo as enum ('one', 'two');
+ CREATE TYPE public.footab as enum ('three', 'four');
+ CREATE DOMAIN id AS int;
+ CREATE FUNCTION f() RETURNS int LANGUAGE SQL RETURN 1;
+ CREATE FUNCTION public.falter() RETURNS int LANGUAGE SQL RETURN 1;
+ CREATE FOREIGN DATA WRAPPER fdw_wrapper;
+}
+
+teardown
+{
+ DROP FUNCTION IF EXISTS testschema.foo();
+ DROP FUNCTION IF EXISTS fooargtype(num foo);
+ DROP FUNCTION IF EXISTS footrettype();
+ DROP FUNCTION IF EXISTS foofunc();
+ DROP FUNCTION IF EXISTS public.falter();
+ DROP FUNCTION IF EXISTS alterschema.falter();
+ DROP DOMAIN IF EXISTS idid;
+ DROP SERVER IF EXISTS srv_fdw_wrapper;
+ DROP TABLE IF EXISTS tabtype;
+ DROP SCHEMA IF EXISTS testschema;
+ DROP SCHEMA IF EXISTS alterschema;
+ DROP TYPE IF EXISTS public.foo;
+ DROP TYPE IF EXISTS public.footab;
+ DROP DOMAIN IF EXISTS id;
+ DROP FUNCTION IF EXISTS f();
+ DROP FOREIGN DATA WRAPPER IF EXISTS fdw_wrapper;
+}
+
+session "s1"
+
+step "s1_begin" { BEGIN; }
+step "s1_create_function_in_schema" { CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; }
+step "s1_create_function_with_argtype" { CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; }
+step "s1_create_function_with_rettype" { CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; }
+step "s1_create_function_with_function" { CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; }
+step "s1_alter_function_schema" { ALTER FUNCTION public.falter() SET SCHEMA alterschema; }
+step "s1_create_domain_with_domain" { CREATE DOMAIN idid as id; }
+step "s1_create_table_with_type" { CREATE TABLE tabtype(a footab); }
+step "s1_create_server_with_fdw_wrapper" { CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+
+step "s2_begin" { BEGIN; }
+step "s2_drop_schema" { DROP SCHEMA testschema; }
+step "s2_drop_alterschema" { DROP SCHEMA alterschema; }
+step "s2_drop_foo_type" { DROP TYPE public.foo; }
+step "s2_drop_foo_rettype" { DROP DOMAIN id; }
+step "s2_drop_footab_type" { DROP TYPE public.footab; }
+step "s2_drop_function_f" { DROP FUNCTION f(); }
+step "s2_drop_domain_id" { DROP DOMAIN id; }
+step "s2_drop_fdw_wrapper" { DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; }
+step "s2_commit" { COMMIT; }
+
+# function - schema
+permutation "s1_begin" "s1_create_function_in_schema" "s2_drop_schema" "s1_commit"
+permutation "s2_begin" "s2_drop_schema" "s1_create_function_in_schema" "s2_commit"
+
+# alter function - schema
+permutation "s1_begin" "s1_alter_function_schema" "s2_drop_alterschema" "s1_commit"
+permutation "s2_begin" "s2_drop_alterschema" "s1_alter_function_schema" "s2_commit"
+
+# function - argtype
+permutation "s1_begin" "s1_create_function_with_argtype" "s2_drop_foo_type" "s1_commit"
+permutation "s2_begin" "s2_drop_foo_type" "s1_create_function_with_argtype" "s2_commit"
+
+# function - rettype
+permutation "s1_begin" "s1_create_function_with_rettype" "s2_drop_foo_rettype" "s1_commit"
+permutation "s2_begin" "s2_drop_foo_rettype" "s1_create_function_with_rettype" "s2_commit"
+
+# function - function
+permutation "s1_begin" "s1_create_function_with_function" "s2_drop_function_f" "s1_commit"
+permutation "s2_begin" "s2_drop_function_f" "s1_create_function_with_function" "s2_commit"
+
+# domain - domain
+permutation "s1_begin" "s1_create_domain_with_domain" "s2_drop_domain_id" "s1_commit"
+permutation "s2_begin" "s2_drop_domain_id" "s1_create_domain_with_domain" "s2_commit"
+
+# table - type
+permutation "s1_begin" "s1_create_table_with_type" "s2_drop_footab_type" "s1_commit"
+permutation "s2_begin" "s2_drop_footab_type" "s1_create_table_with_type" "s2_commit"
+
+# server - foreign data wrapper
+permutation "s1_begin" "s1_create_server_with_fdw_wrapper" "s2_drop_fdw_wrapper" "s1_commit"
+permutation "s2_begin" "s2_drop_fdw_wrapper" "s1_create_server_with_fdw_wrapper" "s2_commit"
diff --git a/src/test/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2c9..df8d276dfc 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -37,6 +37,8 @@ NOTICE: in object access: superuser attempting create (subId=0x0) [internal]
NOTICE: in object access: superuser finished create (subId=0x0) [internal]
NOTICE: in object access: superuser attempting create (subId=0x0) [internal]
NOTICE: in object access: superuser finished create (subId=0x0) [internal]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
NOTICE: in process utility: superuser finished CREATE TABLE
CREATE RULE test_oat_notify AS
ON UPDATE TO test_oat_schema.test_oat_tab
@@ -62,8 +64,6 @@ BEGIN
END IF;
END; $$;
NOTICE: in process utility: superuser attempting CREATE FUNCTION
-NOTICE: in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
-NOTICE: in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
NOTICE: in process utility: superuser finished CREATE FUNCTION
diff --git a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
index effdc49145..da6d931994 100644
--- a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
+++ b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
@@ -86,6 +86,8 @@ NOTICE: in object access: superuser attempting create (subId=0x0) [internal]
NOTICE: in object access: superuser finished create (subId=0x0) [internal]
NOTICE: in object access: superuser attempting create (subId=0x0) [internal]
NOTICE: in object access: superuser finished create (subId=0x0) [internal]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
NOTICE: in process utility: superuser finished CREATE TABLE
CREATE INDEX regress_test_table_t_idx ON regress_test_table (t);
NOTICE: in process utility: superuser attempting CREATE INDEX
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 673361e840..c2115ea601 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2867,11 +2867,12 @@ begin;
alter table alterlock2
add constraint alterlock2nv foreign key (f1) references alterlock (f1) NOT VALID;
select * from my_locks order by 1;
- relname | max_lockmode
-------------+-----------------------
- alterlock | ShareRowExclusiveLock
- alterlock2 | ShareRowExclusiveLock
-(2 rows)
+ relname | max_lockmode
+----------------+-----------------------
+ alterlock | ShareRowExclusiveLock
+ alterlock2 | ShareRowExclusiveLock
+ alterlock_pkey | AccessShareLock
+(3 rows)
commit;
begin;
--
2.34.1
--Bj+x/cSHj0rO0bhZ--
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* [PATCH v2 1/2] Do not lock tables in get_tables_to_repack().
@ 2026-06-16 06:49 ChangAo Chen <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: ChangAo Chen @ 2026-06-16 06:49 UTC (permalink / raw)
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index faa07d1a118..2879c8af574 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2169,22 +2169,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2192,7 +2179,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2201,10 +2187,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2228,45 +2211,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2279,7 +2237,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2357,15 +2315,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.47.3
--op6mnexl7cn72cto
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment; filename=v2-0002-fixups.patch
^ permalink raw reply [nested|flat] 332+ messages in thread
* Do not lock tables in get_tables_to_repack
@ 2026-06-16 07:34 =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
0 siblings, 2 replies; 332+ messages in thread
From: =?utf-8?B?Y2NhNTUwNw==?= @ 2026-06-16 07:34 UTC (permalink / raw)
To: =?utf-8?B?cGdzcWwtaGFja2Vycw==?= <pgsql-hackers@lists.postgresql.org>
Hi hackers,
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
Thoughts?
--
Regards,
ChangAo Chen
Attachments:
[application/octet-stream] v1-0001-Do-not-lock-tables-in-get_tables_to_repack.patch (5.4K, ../../tencent_9F290B256A3F52B66542F1140E32ECC64309@qq.com/2-v1-0001-Do-not-lock-tables-in-get_tables_to_repack.patch)
download | inline diff:
From fa5c3073f2f41cf3c8ab7c9663b681e6140a4824 Mon Sep 17 00:00:00 2001
From: ChangAo Chen <cca5507@qq.com>
Date: Tue, 16 Jun 2026 14:49:49 +0800
Subject: [PATCH v1] Do not lock tables in get_tables_to_repack().
When doing a whole database repack, we build a list of repackable
tables and take a lock on them to prevent concurrent drops. But
concurrent drops can always happen after we build the list because
we process each table in a separate transaction. The
ConditionalLockRelationOid() also makes the default behavior like
SKIP_LOCKED, which is unexpected.
To remove the locks, we need to make repack_is_permitted_for_relation()
handles concurrent drops correctly: it should not report an error
when failing to search the syscache in pg_class_aclcheck(). Use
pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
check the return value of get_rel_name().
While at it, replace relation_close() with table_close() to match
the table_open().
---
src/backend/commands/repack.c | 67 ++++++++++-------------------------
1 file changed, 19 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index ec100e3eef5..6f25effeed2 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -2159,22 +2159,9 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
index = (Form_pg_index) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the index's table, to ensure it
- * doesn't go away while we collect the list. If we cannot, just
- * disregard it. Be sure to release this if we ultimately decide
- * not to process the table!
- */
- if (!ConditionalLockRelationOid(index->indrelid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists; skip if not */
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
if (!HeapTupleIsValid(classtup))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
classForm = (Form_pg_class) GETSTRUCT(classtup);
/* Skip temp relations belonging to other sessions */
@@ -2182,7 +2169,6 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
!isTempOrTempToastNamespace(classForm->relnamespace))
{
ReleaseSysCache(classtup);
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
}
@@ -2191,10 +2177,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
GetUserId()))
- {
- UnlockRelationOid(index->indrelid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2218,45 +2201,20 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
class = (Form_pg_class) GETSTRUCT(tuple);
- /*
- * Try to obtain a light lock on the table, to ensure it doesn't
- * go away while we collect the list. If we cannot, just
- * disregard the table. Be sure to release this if we ultimately
- * decide not to process the table!
- */
- if (!ConditionalLockRelationOid(class->oid, AccessShareLock))
- continue;
-
- /* Verify that the table still exists */
- if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(class->oid)))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
- continue;
- }
-
/* Can only process plain tables and matviews */
if (class->relkind != RELKIND_RELATION &&
class->relkind != RELKIND_MATVIEW)
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Skip temp relations belonging to other sessions */
if (class->relpersistence == RELPERSISTENCE_TEMP &&
!isTempOrTempToastNamespace(class->relnamespace))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
GetUserId()))
- {
- UnlockRelationOid(class->oid, AccessShareLock);
continue;
- }
/* Use a permanent memory context for the result list */
oldcxt = MemoryContextSwitchTo(permcxt);
@@ -2269,7 +2227,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
}
table_endscan(scan);
- relation_close(catalog, AccessShareLock);
+ table_close(catalog, AccessShareLock);
return rtcs;
}
@@ -2347,15 +2305,28 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
static bool
repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
{
+ bool is_missing = false;
+
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
+ if (pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
return true;
- ereport(WARNING,
- errmsg("permission denied to execute %s on \"%s\", skipping it",
- RepackCommandAsString(cmd),
- get_rel_name(relid)));
+ /* Report a warning if the relation still exists. */
+ if (!is_missing)
+ {
+ char *relname;
+
+ relname = get_rel_name(relid);
+ if (relname != NULL)
+ {
+ ereport(WARNING,
+ errmsg("permission denied to execute %s on \"%s\", skipping it",
+ RepackCommandAsString(cmd), relname));
+
+ pfree(relname);
+ }
+ }
return false;
}
--
2.34.1
^ permalink raw reply [nested|flat] 332+ messages in thread
* Re: Do not lock tables in get_tables_to_repack
@ 2026-06-27 19:56 Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
parent: =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
1 sibling, 0 replies; 332+ messages in thread
From: Bharath Rupireddy @ 2026-06-27 19:56 UTC (permalink / raw)
To: cca5507 <cca5507@qq.com>; +Cc: pgsql-hackers <pgsql-hackers@lists.postgresql.org>; Antonin Houska <ah@cybertec.at>; Alvaro Herrera <alvherre@alvh.no-ip.org>
Hi,
On Tue, Jun 16, 2026 at 12:35 AM cca5507 <cca5507@qq.com> wrote:
>
> Hi hackers,
>
> When doing a whole database repack, we build a list of repackable
> tables and take a lock on them to prevent concurrent drops. But
> concurrent drops can always happen after we build the list because
> we process each table in a separate transaction. The
> ConditionalLockRelationOid() also makes the default behavior like
> SKIP_LOCKED, which is unexpected.
>
> To remove the locks, we need to make repack_is_permitted_for_relation()
> handles concurrent drops correctly: it should not report an error
> when failing to search the syscache in pg_class_aclcheck(). Use
> pg_class_aclcheck_ext() instead to detect a concurrent drop. Also
> check the return value of get_rel_name().
>
> Thoughts?
I don't have a strong opinion on fixing this for REPACK. For vacuum,
we wanted to get consistent behavior across all modes of invoking it
[1]. Whether to handle concurrent drops for REPACK depends on how long
it spends scanning pg_index/pg_class in get_tables_to_repack. Also,
vacuum is more commonly run database-wide (against all tables), and
without field experience showing this is a real problem for REPACK,
I'm not sure the fix is needed. That said, others may have a different
take - adding the REPACK authors here for their input.
[1] https://www.postgresql.org/message-id/CALj2ACUbhDFJWSwiyoUxXED2S2fr9Xis%3D08Rnjq53UzBE4FvzA%40mail.g...
--
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 332+ messages in thread
* Re: Do not lock tables in get_tables_to_repack
@ 2026-07-07 16:53 Álvaro Herrera <alvherre@kurilemu.de>
parent: =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
1 sibling, 1 reply; 332+ messages in thread
From: Álvaro Herrera @ 2026-07-07 16:53 UTC (permalink / raw)
To: cca5507 <cca5507@qq.com>; +Cc: pgsql-hackers <pgsql-hackers@lists.postgresql.org>
On 2026-Jun-16, cca5507 wrote:
> Hi hackers,
>
> When doing a whole database repack, we build a list of repackable
> tables and take a lock on them to prevent concurrent drops. But
> concurrent drops can always happen after we build the list because
> we process each table in a separate transaction.
Not only that. We have actually three ways to obtain the list of tables
to repack, and only one of these obtains the locks. So this code is
internally inconsistent. I agree that we should do something like your
patch. I wanted to be a little more defensive though; how about the
attached?
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
^ permalink raw reply [nested|flat] 332+ messages in thread
* Re: Do not lock tables in get_tables_to_repack
@ 2026-07-08 02:36 =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
parent: Álvaro Herrera <alvherre@kurilemu.de>
0 siblings, 1 reply; 332+ messages in thread
From: =?utf-8?B?Y2NhNTUwNw==?= @ 2026-07-08 02:36 UTC (permalink / raw)
To: =?utf-8?B?w4FsdmFybyBIZXJyZXJh?= <alvherre@kurilemu.de>; +Cc: =?utf-8?B?cGdzcWwtaGFja2Vycw==?= <pgsql-hackers@lists.postgresql.org>
> > Hi hackers,
> >
> > When doing a whole database repack, we build a list of repackable
> > tables and take a lock on them to prevent concurrent drops. But
> > concurrent drops can always happen after we build the list because
> > we process each table in a separate transaction.
>
> Not only that. We have actually three ways to obtain the list of tables
> to repack, and only one of these obtains the locks. So this code is
> internally inconsistent. I agree that we should do something like your
> patch. I wanted to be a little more defensive though; how about the
> attached?
- classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
+ classtup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(index->indrelid));
Do we really need to copy it? We hold a refcount on it so it won't be freed
until we release it. Otherwise LGTM.
--
Regards,
ChangAo Chen
^ permalink raw reply [nested|flat] 332+ messages in thread
* Re: Do not lock tables in get_tables_to_repack
@ 2026-07-10 14:22 Álvaro Herrera <alvherre@kurilemu.de>
parent: =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
0 siblings, 1 reply; 332+ messages in thread
From: Álvaro Herrera @ 2026-07-10 14:22 UTC (permalink / raw)
To: cca5507 <cca5507@qq.com>; +Cc: pgsql-hackers <pgsql-hackers@lists.postgresql.org>
On 2026-Jul-08, cca5507 wrote:
> - classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
> + classtup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(index->indrelid));
>
> Do we really need to copy it? We hold a refcount on it so it won't be freed
> until we release it. Otherwise LGTM.
Yeah, I guess it doesn't matter. I have removed the copy and updated
some comments. I also realized that there are some places where we
weren't dealing correctly with the possibility that the relation goes
away, or is replaced with something different, so I added that too.
While looking at it I also realized that get_tables_to_repack_partitioned
is likewise not careful enough about it: we do IndexGetRelation(, false)
which fails hard if the pg_index tuple cannot be found, which is the
wrong thing to do.
At the same time, it's annoying that half of the code that clearly
belongs in that routine is actually in ExecRepack(). I moved that to
where it rightfully belongs. (The only somewhat annoying thing is that
we have to NULL-out the Relation pointer after returning; but that's not
*too* bad IMO.)
Any opinions on this?
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"No me acuerdo, pero no es cierto. No es cierto, y si fuera cierto,
no me acuerdo." (Augusto Pinochet a una corte de justicia)
^ permalink raw reply [nested|flat] 332+ messages in thread
* Re: Do not lock tables in get_tables_to_repack
@ 2026-07-10 16:03 Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
parent: Álvaro Herrera <alvherre@kurilemu.de>
0 siblings, 1 reply; 332+ messages in thread
From: Bharath Rupireddy @ 2026-07-10 16:03 UTC (permalink / raw)
To: Álvaro Herrera <alvherre@kurilemu.de>; +Cc: cca5507 <cca5507@qq.com>; pgsql-hackers <pgsql-hackers@lists.postgresql.org>
Hi,
On Fri, Jul 10, 2026 at 7:22 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
>
> On 2026-Jul-08, cca5507 wrote:
>
> > - classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(index->indrelid));
> > + classtup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(index->indrelid));
> >
> > Do we really need to copy it? We hold a refcount on it so it won't be freed
> > until we release it. Otherwise LGTM.
Thanks for committing this.
Here is my overall take. Even before this commit, a database-wide
REPACK does not error on a concurrently dropped table.
get_tables_to_repack takes a brief lock on each table before the ACL
check, which blocks a concurrent drop while the list is being built.
Later each table is re-opened under its lock and skipped if it is
already gone. So the brief lock is not needed for correctness. What it
does is let an unprivileged user lock relations it may not even be
allowed to repack, including system catalogs, before we have checked
its privileges. I am fine with removing the lock as the commit did.
I would also like to tighten repack_is_permitted_for_relation so a
caller that holds a lock still errors out on a missing relation
instead of skipping it silently
(https://www.postgresql.org/message-id/akPhEffRipH4isWF%40nathan).
Attached as a follow-up patch. Please have a look.
> Yeah, I guess it doesn't matter. I have removed the copy and updated
> some comments. I also realized that there are some places where we
> weren't dealing correctly with the possibility that the relation goes
> away, or is replaced with something different, so I added that too.
I agree that the SearchSysCacheCopy1 in v2-0002 adds no benefit. Since
no lock is held on the relation, there is no point in copying the
syscache tuple.
> While looking at it I also realized that get_tables_to_repack_partitioned
> is likewise not careful enough about it: we do IndexGetRelation(, false)
> which fails hard if the pg_index tuple cannot be found, which is the
> wrong thing to do.
>
> At the same time, it's annoying that half of the code that clearly
> belongs in that routine is actually in ExecRepack(). I moved that to
> where it rightfully belongs. (The only somewhat annoying thing is that
> we have to NULL-out the Relation pointer after returning; but that's not
> *too* bad IMO.)
>
> Any opinions on this?
I will take a closer look at this in a bit.
--
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com
Attachments:
[application/octet-stream] v1-0001-Tighten-ACL-check-in-repack_is_permitted_for_rela.patch (5.1K, ../../CALj2ACX3pyuRS8++6L20cJUMRTf_qbbVp69J1btJ3y6=77e5gw@mail.gmail.com/2-v1-0001-Tighten-ACL-check-in-repack_is_permitted_for_rela.patch)
download | inline diff:
From 6729b2f14ed623a8b38f5a530b8c85c96bfd5b40 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Fri, 10 Jul 2026 15:42:47 +0000
Subject: [PATCH v1] Tighten ACL check in repack_is_permitted_for_relation()
Commit c71d43025d7 made repack_is_permitted_for_relation() use
_ext() and skip a concurrently dropped relation silently. That is
fine for the callers that hold no lock on the relation, but it is
applied unconditionally, including where we may already hold a
lock on the relation whose ACL is checked. In that case missing a
relation is not fine, so this made the code more brittle: a
missing relation there would indicate a bug and should error out
rather than be skipped silently.
Add a missing_ok flag so the silent skip is done only where no
lock is held. Callers that hold a lock pass false and still error
out on a missing relation.
---
src/backend/commands/repack.c | 38 ++++++++++++++++++++++++-----------
1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 02883fe34a4..b09be65da3b 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -172,8 +172,8 @@ static List *get_tables_to_repack(RepackCommand cmd, bool usingindex,
static List *get_tables_to_repack_partitioned(RepackCommand cmd,
Oid relid, bool rel_is_index,
MemoryContext permcxt);
-static bool repack_is_permitted_for_relation(RepackCommand cmd,
- Oid relid, Oid userid);
+static bool repack_is_permitted_for_relation(RepackCommand cmd, Oid relid,
+ Oid userid, bool missing_ok);
static void apply_concurrent_changes(BufFile *file, ChangeContext *chgcxt);
static void apply_concurrent_insert(Relation rel, TupleTableSlot *slot,
@@ -724,7 +724,7 @@ cluster_rel_recheck(RepackCommand cmd, Relation OldHeap, Oid indexOid,
Assert(CheckRelationLockedByMe(OldHeap, lmode, false));
/* Check that the user still has privileges for the relation */
- if (!repack_is_permitted_for_relation(cmd, tableOid, userid))
+ if (!repack_is_permitted_for_relation(cmd, tableOid, userid, false))
{
relation_close(OldHeap, lmode);
return false;
@@ -2198,7 +2198,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, index->indrelid,
- GetUserId()))
+ GetUserId(), true))
continue;
/* Use a permanent memory context for the result list */
@@ -2235,7 +2235,7 @@ get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
/* noisily skip rels which the user can't process */
if (!repack_is_permitted_for_relation(cmd, class->oid,
- GetUserId()))
+ GetUserId(), true))
continue;
/* Use a permanent memory context for the result list */
@@ -2304,7 +2304,7 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
* leaf partition despite having them on the partitioned table. Skip
* if so.
*/
- if (!repack_is_permitted_for_relation(cmd, table_oid, GetUserId()))
+ if (!repack_is_permitted_for_relation(cmd, table_oid, GetUserId(), true))
continue;
/* Use a permanent memory context for the result list */
@@ -2321,16 +2321,21 @@ get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid,
/*
- * Return whether userid has privileges to execute REPACK on relid.
+ * Check whether the passed-in user has privileges to execute REPACK on the
+ * relation.
*
- * Caller may not have a lock on the relation, so it could have been
- * dropped concurrently. In that case, silently return false.
+ * missing_ok should be passed only by callers that hold no lock on the
+ * relation, where a concurrent drop is possible and fine to skip. A caller
+ * that holds a lock must not pass it: there a missing relation would indicate
+ * a bug and should error out rather than be silently skipped.
*
* If the relation does exist but the user doesn't have the required
- * privs, emit a WARNING and return false. Otherwise, return true.
+ * privileges, emit a WARNING and return false to let the caller
+ * decide what to do with this relation.
*/
static bool
-repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
+repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid,
+ bool missing_ok)
{
bool is_missing = false;
AclResult result;
@@ -2338,9 +2343,18 @@ repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
Assert(cmd == REPACK_COMMAND_CLUSTER || cmd == REPACK_COMMAND_REPACK);
- result = pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN, &is_missing);
+ result = pg_class_aclcheck_ext(relid, userid, ACL_MAINTAIN,
+ missing_ok ? &is_missing : NULL);
+
+ /*
+ * If the relation was concurrently dropped, nothing to do. Note that this
+ * is only reachable when the caller specified missing_ok.
+ */
if (is_missing)
+ {
+ Assert(missing_ok);
return false;
+ }
if (result == ACLCHECK_OK)
return true;
--
2.47.3
^ permalink raw reply [nested|flat] 332+ messages in thread
* Re: Do not lock tables in get_tables_to_repack
@ 2026-07-10 17:34 Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
parent: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
0 siblings, 1 reply; 332+ messages in thread
From: Bharath Rupireddy @ 2026-07-10 17:34 UTC (permalink / raw)
To: Álvaro Herrera <alvherre@kurilemu.de>; +Cc: cca5507 <cca5507@qq.com>; pgsql-hackers <pgsql-hackers@lists.postgresql.org>
Hi,
On Fri, Jul 10, 2026 at 9:03 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:
>
> > While looking at it I also realized that get_tables_to_repack_partitioned
> > is likewise not careful enough about it: we do IndexGetRelation(, false)
> > which fails hard if the pg_index tuple cannot be found, which is the
> > wrong thing to do.
Agreed. For a single-level partitioned table, dropping a leaf blocks
on the parent's AccessExclusiveLock, which REPACK holds, so the leaf
can't go away mid-build. But for a multi-level table, REPACK holds a
lock only on the top parent, so a lower-level leaf whose immediate
parent is an intermediate table isn't protected and can be dropped
concurrently. So IndexGetRelation(, false) failing hard there is
indeed the wrong thing.
> > At the same time, it's annoying that half of the code that clearly
> > belongs in that routine is actually in ExecRepack(). I moved that to
> > where it rightfully belongs.
+1, that's a good cleanup. That code clearly belongs in
get_tables_to_repack_partitioned().
>> (The only somewhat annoying thing is that
> > we have to NULL-out the Relation pointer after returning; but that's not
> > *too* bad IMO.)
That's fine. We already NULL out rel after
get_tables_to_repack_partitioned() today, so the patch isn't adding
anything new there. Relying on a non-NULL return from
process_single_relation() to mean partitioned table is a little
indirect on first read, but the comments explains it well, and I
wouldn't complicate the code to avoid it.
> > Any opinions on this?
>
> I will take a closer look at this in a bit.
Patch looks good to me. One nit:
+ table_oid = IndexGetRelation(child_oid, true);
+ if (!OidIsValid(table_oid))
+ continue;
How about a short comment on why this is needed even with a lock on
the parent, since that lock doesn't cover lower-level leaves in a
multi-level partition tree?
--
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com
^ permalink raw reply [nested|flat] 332+ messages in thread
* Re: Do not lock tables in get_tables_to_repack
@ 2026-07-11 14:50 =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
parent: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
0 siblings, 1 reply; 332+ messages in thread
From: =?utf-8?B?Y2NhNTUwNw==?= @ 2026-07-11 14:50 UTC (permalink / raw)
To: =?utf-8?B?QmhhcmF0aCBSdXBpcmVkZHk=?= <bharath.rupireddyforpostgres@gmail.com>; =?utf-8?B?w4FsdmFybyBIZXJyZXJh?= <alvherre@kurilemu.de>; +Cc: =?utf-8?B?cGdzcWwtaGFja2Vycw==?= <pgsql-hackers@lists.postgresql.org>
> > > Any opinions on this?
> >
> > I will take a closer look at this in a bit.
>
> Patch looks good to me. One nit:
>
> + table_oid = IndexGetRelation(child_oid, true);
> + if (!OidIsValid(table_oid))
> + continue;
>
> How about a short comment on why this is needed even with a lock on
> the parent, since that lock doesn't cover lower-level leaves in a
> multi-level partition tree?
I think it's worth a comment, too. Patch LGTM.
--
Regards,
ChangAo Chen
^ permalink raw reply [nested|flat] 332+ messages in thread
* Re: Do not lock tables in get_tables_to_repack
@ 2026-07-20 10:19 Álvaro Herrera <alvherre@kurilemu.de>
parent: =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
0 siblings, 0 replies; 332+ messages in thread
From: Álvaro Herrera @ 2026-07-20 10:19 UTC (permalink / raw)
To: cca5507 <cca5507@qq.com>; +Cc: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>; pgsql-hackers <pgsql-hackers@lists.postgresql.org>
On 2026-Jul-11, cca5507 wrote:
> Bharath Rupireddy wrote:
> > + table_oid = IndexGetRelation(child_oid, true);
> > + if (!OidIsValid(table_oid))
> > + continue;
> >
> > How about a short comment on why this is needed even with a lock on
> > the parent, since that lock doesn't cover lower-level leaves in a
> > multi-level partition tree?
>
> I think it's worth a comment, too. Patch LGTM.
Thanks, added that and pushed.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"Puedes vivir sólo una vez, pero si lo haces bien, una vez es suficiente"
^ permalink raw reply [nested|flat] 332+ messages in thread
end of thread, other threads:[~2026-07-20 10:19 UTC | newest]
Thread overview: 332+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2024-03-29 15:43 [PATCH v15] Avoid orphaned objects dependencies Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 06:49 [PATCH v2 1/2] Do not lock tables in get_tables_to_repack(). ChangAo Chen <cca5507@qq.com>
2026-06-16 07:34 Do not lock tables in get_tables_to_repack =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
2026-06-27 19:56 ` Re: Do not lock tables in get_tables_to_repack Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
2026-07-07 16:53 ` Re: Do not lock tables in get_tables_to_repack Álvaro Herrera <alvherre@kurilemu.de>
2026-07-08 02:36 ` Re: Do not lock tables in get_tables_to_repack =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
2026-07-10 14:22 ` Re: Do not lock tables in get_tables_to_repack Álvaro Herrera <alvherre@kurilemu.de>
2026-07-10 16:03 ` Re: Do not lock tables in get_tables_to_repack Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
2026-07-10 17:34 ` Re: Do not lock tables in get_tables_to_repack Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
2026-07-11 14:50 ` Re: Do not lock tables in get_tables_to_repack =?utf-8?B?Y2NhNTUwNw==?= <cca5507@qq.com>
2026-07-20 10:19 ` Re: Do not lock tables in get_tables_to_repack Álvaro Herrera <alvherre@kurilemu.de>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox