rangevargetrelid-callback.patch
application/octet-stream
Filename: rangevargetrelid-callback.patch
Type: application/octet-stream
Part: 0
Message:
Re: RangeVarGetRelid()
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/catalog/index.c | 1 | 2 |
| src/backend/catalog/namespace.c | 19 | 2 |
| src/backend/commands/indexcmds.c | 53 | 25 |
| src/include/catalog/index.h | 1 | 0 |
| src/include/catalog/namespace.h | 8 | 2 |
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 99e130c..2eab82b 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -111,7 +111,6 @@ static void validate_index_heapscan(Relation heapRelation,
IndexInfo *indexInfo,
Snapshot snapshot,
v_i_state *state);
-static Oid IndexGetRelation(Oid indexId);
static bool ReindexIsCurrentlyProcessingIndex(Oid indexOid);
static void SetReindexProcessing(Oid heapOid, Oid indexOid);
static void ResetReindexProcessing(void);
@@ -2763,7 +2762,7 @@ validate_index_heapscan(Relation heapRelation,
* IndexGetRelation: given an index's relation OID, get the OID of the
* relation it is an index on. Uses the system cache.
*/
-static Oid
+Oid
IndexGetRelation(Oid indexId)
{
HeapTuple tuple;
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 6d4d4b1..c74168c 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -219,10 +219,14 @@ Datum pg_is_other_temp_schema(PG_FUNCTION_ARGS);
* otherwise raise an error.
*
* If nowait = true, throw an error if we'd have to wait for a lock.
+ *
+ * Callback allows caller to check permissions or acquire additional locks
+ * prior to grabbing the relation lock.
*/
Oid
-RangeVarGetRelid(const RangeVar *relation, LOCKMODE lockmode, bool missing_ok,
- bool nowait)
+RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
+ bool missing_ok, bool nowait,
+ RangeVarGetRelidCallback callback)
{
uint64 inval_count;
Oid relId;
@@ -336,6 +340,19 @@ RangeVarGetRelid(const RangeVar *relation, LOCKMODE lockmode, bool missing_ok,
}
/*
+ * Invoke caller-supplied callback, if any.
+ *
+ * This callback is a good place to check permissions: we haven't taken
+ * the table lock yet (and it's really best to check permissions before
+ * locking anything!), but we've gotten far enough to know what OID we
+ * think we should lock. Of course, concurrent DDL might things while
+ * we're waiting for the lock, but in that case the callback will be
+ * invoked again for the new OID.
+ */
+ if (callback)
+ callback(relation, relId, oldRelId);
+
+ /*
* Lock relation. This will also accept any pending invalidation
* messages. If we got back InvalidOid, indicating not found, then
* there's nothing to lock, but we accept invalidation messages
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 69aa5bf..174a194 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -63,7 +63,10 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
static Oid GetIndexOpClass(List *opclass, Oid attrType,
char *accessMethodName, Oid accessMethodId);
static char *ChooseIndexNameAddition(List *colnames);
-
+static void RangeVarCallbackForReindexTable(const RangeVar *relation,
+ Oid relId, Oid oldRelId);
+static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
+ Oid relId, Oid oldRelId);
/*
* CheckIndexCompatible
@@ -1725,33 +1728,47 @@ void
ReindexIndex(RangeVar *indexRelation)
{
Oid indOid;
+
+ /* lock level used here should match index lock reindex_index() */
+ indOid = RangeVarGetRelidExtended(indexRelation, AccessExclusiveLock,
+ false, false,
+ RangeVarCallbackForReindexIndex);
+
+ reindex_index(indOid, false);
+}
+
+/*
+ * Check permissions on table before acquiring relation lock; also lock
+ * the heap before the RangeVarGetRelid() takes the index lock, to avoid
+ * deadlocks.
+ */
+static void
+RangeVarCallbackForReindexIndex(const RangeVar *relation,
+ Oid relId, Oid oldRelId)
+{
HeapTuple tuple;
- /*
- * XXX: This is not safe in the presence of concurrent DDL. We should
- * take AccessExclusiveLock here, but that would violate the rule that
- * indexes should only be locked after their parent tables. For now,
- * we live with it.
- */
- indOid = RangeVarGetRelid(indexRelation, NoLock, false, false);
- tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(indOid));
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
if (!HeapTupleIsValid(tuple))
- elog(ERROR, "cache lookup failed for relation %u", indOid);
+ elog(ERROR, "cache lookup failed for relation %u", relId);
if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not an index",
- indexRelation->relname)));
+ relation->relname)));
/* Check permissions */
- if (!pg_class_ownercheck(indOid, GetUserId()))
+ if (!pg_class_ownercheck(relId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- indexRelation->relname);
+ relation->relname);
- ReleaseSysCache(tuple);
+ /* lock levels here should match reindex_index() heap lock */
+ if (OidIsValid(oldRelId))
+ UnlockRelationOid(IndexGetRelation(oldRelId), ShareLock);
+ LockRelationOid(IndexGetRelation(relId), ShareLock);
- reindex_index(indOid, false);
+ ReleaseSysCache(tuple);
}
/*
@@ -1762,13 +1779,29 @@ void
ReindexTable(RangeVar *relation)
{
Oid heapOid;
- HeapTuple tuple;
/* The lock level used here should match reindex_relation(). */
- heapOid = RangeVarGetRelid(relation, ShareLock, false, false);
- tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(heapOid));
+ heapOid = RangeVarGetRelidExtended(relation, ShareLock, false, false,
+ RangeVarCallbackForReindexTable);
+
+ if (!reindex_relation(heapOid, REINDEX_REL_PROCESS_TOAST))
+ ereport(NOTICE,
+ (errmsg("table \"%s\" has no indexes",
+ relation->relname)));
+}
+
+/*
+ * Check permissions on table before acquiring relation lock.
+ */
+static void
+RangeVarCallbackForReindexTable(const RangeVar *relation,
+ Oid relId, Oid oldRelId)
+{
+ HeapTuple tuple;
+
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
- elog(ERROR, "cache lookup failed for relation %u", heapOid);
+ elog(ERROR, "cache lookup failed for relation %u", relId);
if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION &&
((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_TOASTVALUE)
@@ -1778,16 +1811,11 @@ ReindexTable(RangeVar *relation)
relation->relname)));
/* Check permissions */
- if (!pg_class_ownercheck(heapOid, GetUserId()))
+ if (!pg_class_ownercheck(relId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
relation->relname);
ReleaseSysCache(tuple);
-
- if (!reindex_relation(heapOid, REINDEX_REL_PROCESS_TOAST))
- ereport(NOTICE,
- (errmsg("table \"%s\" has no indexes",
- relation->relname)));
}
/*
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 8b78b05..5b09b9f 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -99,5 +99,6 @@ extern bool reindex_relation(Oid relid, int flags);
extern bool ReindexIsProcessingHeap(Oid heapOid);
extern bool ReindexIsProcessingIndex(Oid indexOid);
+extern Oid IndexGetRelation(Oid indexId);
#endif /* INDEX_H */
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 904c6fd..97f8e72 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -47,9 +47,15 @@ typedef struct OverrideSearchPath
bool addTemp; /* implicitly prepend temp schema? */
} OverrideSearchPath;
+typedef void (*RangeVarGetRelidCallback)(const RangeVar *relation, Oid relId,
+ Oid oldRelId);
-extern Oid RangeVarGetRelid(const RangeVar *relation, LOCKMODE lockmode,
- bool missing_ok, bool nowait);
+#define RangeVarGetRelid(relation, lockmode, missing_ok, nowait) \
+ RangeVarGetRelidExtended(relation, lockmode, missing_ok, nowait, NULL)
+
+extern Oid RangeVarGetRelidExtended(const RangeVar *relation,
+ LOCKMODE lockmode, bool missing_ok, bool nowait,
+ RangeVarGetRelidCallback callback);
extern Oid RangeVarGetCreationNamespace(const RangeVar *newRelation);
extern Oid RangeVarGetAndCheckCreationNamespace(const RangeVar *newRelation);
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);