From 80d76be51cfb7f8f54e779ec5a287d80ac28b6e4 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 28 Feb 2020 20:28:34 -0500 Subject: [PATCH] Avoid failure if autovacuum tries to access a just-dropped temp namespace. Such an access became possible when commit 246a6c8f7 added more aggressive cleanup of orphaned temp relations by autovacuum. Since autovacuum's snapshot might be slightly stale, it could attempt to access an already-dropped temp namespace, resulting in an assertion failure or null-pointer dereference. (In practice, since we don't drop temp namespaces automatically but merely recycle them, this situation could only arise if a superuser does a manual drop of a temp namespace. Still, that should be allowed.) The core of the bug, IMO, is that isTempNamespaceInUse and its callers failed to think hard about whether to treat "temp namespace isn't there" differently from "temp namespace isn't in use". In hopes of forestalling future mistakes of the same ilk, replace that function with a new one checkTempNamespaceStatus, which makes the same tests but returns a three-way enum rather than just a bool. isTempNamespaceInUse is gone entirely in HEAD; but just in case some external code is relying on it, keep it in the back branches, as a bug-compatible wrapper around the new function. Per report originally from Prabhat Kumar Sahu, investigated by Mahendra Singh and Michael Paquier; the final form of the patch is my fault. This replaces the failed fix attempt in a052f6cbb. Backpatch as far as v11, as 246a6c8f7 was. Discussion: https://postgr.es/m/CAKYtNAr9Zq=1-ww4etHo-VCC-k120YxZy5OS01VkaLPaDbv2tg@mail.gmail.com --- src/backend/catalog/namespace.c | 20 ++++++++++---------- src/backend/postmaster/autovacuum.c | 7 ++++--- src/include/catalog/namespace.h | 12 +++++++++++- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index e70243a008..5ff78248e0 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -3217,7 +3217,7 @@ isOtherTempNamespace(Oid namespaceId) } /* - * isTempNamespaceInUse - is the given namespace owned and actively used + * checkTempNamespaceStatus - is the given namespace owned and actively used * by a backend? * * Note: this can be used while scanning relations in pg_class to detect @@ -3225,8 +3225,8 @@ isOtherTempNamespace(Oid namespaceId) * given database. The result may be out of date quickly, so the caller * must be careful how to handle this information. */ -bool -isTempNamespaceInUse(Oid namespaceId) +TempNamespaceStatus +checkTempNamespaceStatus(Oid namespaceId) { PGPROC *proc; int backendId; @@ -3235,25 +3235,25 @@ isTempNamespaceInUse(Oid namespaceId) backendId = GetTempNamespaceBackendId(namespaceId); - /* No such temporary namespace? */ + /* No such namespace, or its name shows it's not temp? */ if (backendId == InvalidBackendId) - return false; + return TEMP_NAMESPACE_NOT_TEMP; /* Is the backend alive? */ proc = BackendIdGetProc(backendId); if (proc == NULL) - return false; + return TEMP_NAMESPACE_IDLE; /* Is the backend connected to the same database we are looking at? */ if (proc->databaseId != MyDatabaseId) - return false; + return TEMP_NAMESPACE_IDLE; /* Does the backend own the temporary namespace? */ if (proc->tempNamespaceId != namespaceId) - return false; + return TEMP_NAMESPACE_IDLE; - /* all good to go */ - return true; + /* Yup, so namespace is busy */ + return TEMP_NAMESPACE_IN_USE; } /* diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 6d1f28c327..e3a43d3296 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -2071,9 +2071,10 @@ do_autovacuum(void) { /* * We just ignore it if the owning backend is still active and - * using the temporary schema. + * using the temporary schema. Also, for safety, ignore it if the + * namespace doesn't exist or isn't a temp namespace after all. */ - if (!isTempNamespaceInUse(classForm->relnamespace)) + if (checkTempNamespaceStatus(classForm->relnamespace) == TEMP_NAMESPACE_IDLE) { /* * The table seems to be orphaned -- although it might be that @@ -2243,7 +2244,7 @@ do_autovacuum(void) continue; } - if (isTempNamespaceInUse(classForm->relnamespace)) + if (checkTempNamespaceStatus(classForm->relnamespace) != TEMP_NAMESPACE_IDLE) { UnlockRelationOid(relid, AccessExclusiveLock); continue; diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index d67f93ae3b..3e3a192471 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -37,6 +37,16 @@ typedef struct _FuncCandidateList Oid args[FLEXIBLE_ARRAY_MEMBER]; /* arg types */ } *FuncCandidateList; +/* + * Result of checkTempNamespaceStatus + */ +typedef enum TempNamespaceStatus +{ + TEMP_NAMESPACE_NOT_TEMP, /* nonexistent, or non-temp namespace */ + TEMP_NAMESPACE_IDLE, /* exists, belongs to no active session */ + TEMP_NAMESPACE_IN_USE /* belongs to some active session */ +} TempNamespaceStatus; + /* * Structure for xxxOverrideSearchPath functions */ @@ -138,7 +148,7 @@ extern bool isTempToastNamespace(Oid namespaceId); extern bool isTempOrTempToastNamespace(Oid namespaceId); extern bool isAnyTempNamespace(Oid namespaceId); extern bool isOtherTempNamespace(Oid namespaceId); -extern bool isTempNamespaceInUse(Oid namespaceId); +extern TempNamespaceStatus checkTempNamespaceStatus(Oid namespaceId); extern int GetTempNamespaceBackendId(Oid namespaceId); extern Oid GetTempToastNamespace(void); extern void GetTempNamespaceState(Oid *tempNamespaceId,