diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 6db624109797fc409973ad3af670a0882982a0c7..0b3332ecf5dd0211de5ade4e601801ab0a064bfb 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -1131,13 +1131,7 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 
 	/* Make note that we've accessed a temporary relation */
 	if (RelationUsesLocalBuffers(r))
-	{
-		if (IsParallelWorker())
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
-					 errmsg("cannot access temporary tables during a parallel operation")));
 		MyXactAccessedTempRel = true;
-	}
 
 	pgstat_initstats(r);
 
@@ -1183,13 +1177,7 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 
 	/* Make note that we've accessed a temporary relation */
 	if (RelationUsesLocalBuffers(r))
-	{
-		if (IsParallelWorker())
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
-					 errmsg("cannot access temporary tables during a parallel operation")));
 		MyXactAccessedTempRel = true;
-	}
 
 	pgstat_initstats(r);
 
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index 74a483e0fd9f4efac7874c46e7c23406bc1e3ee5..ab5ef2573cffd6184a12cf94ed09e2d2fe00bdf7 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -17,6 +17,7 @@
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/parallel.h"
+#include "catalog/namespace.h"
 #include "commands/async.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
@@ -67,6 +68,8 @@ typedef struct FixedParallelState
 	Oid			database_id;
 	Oid			authenticated_user_id;
 	Oid			current_user_id;
+	Oid			temp_namespace_id;
+	Oid			temp_toast_namespace_id;
 	int			sec_context;
 	PGPROC	   *parallel_master_pgproc;
 	pid_t		parallel_master_pid;
@@ -288,6 +291,8 @@ InitializeParallelDSM(ParallelContext *pcxt)
 	fps->database_id = MyDatabaseId;
 	fps->authenticated_user_id = GetAuthenticatedUserId();
 	GetUserIdAndSecContext(&fps->current_user_id, &fps->sec_context);
+	GetTempNamespaceState(&fps->temp_namespace_id,
+						  &fps->temp_toast_namespace_id);
 	fps->parallel_master_pgproc = MyProc;
 	fps->parallel_master_pid = MyProcPid;
 	fps->parallel_master_backend_id = MyBackendId;
@@ -1019,6 +1024,13 @@ ParallelWorkerMain(Datum main_arg)
 	/* Restore user ID and security context. */
 	SetUserIdAndSecContext(fps->current_user_id, fps->sec_context);
 
+	/* Restore temp-namespace state to ensure search path matches leader's. */
+	SetTempNamespaceState(fps->temp_namespace_id,
+						  fps->temp_toast_namespace_id);
+
+	/* Set ParallelMasterBackendId so we know how to address temp relations. */
+	ParallelMasterBackendId = fps->parallel_master_backend_id;
+
 	/*
 	 * We've initialized all of our state now; nothing should change
 	 * hereafter.
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index d1cf45bef47b1b285a445bb09194edbdbbd14194..1baaa0bb8988cb348e03dcd38f64706a47e385d2 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -390,7 +390,7 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 	switch (relpersistence)
 	{
 		case RELPERSISTENCE_TEMP:
-			backend = MyBackendId;
+			backend = BackendIdForTempRelations();
 			break;
 		case RELPERSISTENCE_UNLOGGED:
 		case RELPERSISTENCE_PERMANENT:
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index a1aba8ee556bf810214906d73d3b181215b0987d..8fd4c3136bc44726c4d848e1935be9977a30c6e9 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -3073,6 +3073,51 @@ GetTempToastNamespace(void)
 }
 
 
+/*
+ * GetTempNamespaceState - fetch status of session's temporary namespace
+ *
+ * This is used for conveying state to a parallel worker, and is not meant
+ * for general-purpose access.
+ */
+void
+GetTempNamespaceState(Oid *tempNamespaceId, Oid *tempToastNamespaceId)
+{
+	/* Return namespace OIDs, or 0 if session has not created temp namespace */
+	*tempNamespaceId = myTempNamespace;
+	*tempToastNamespaceId = myTempToastNamespace;
+}
+
+/*
+ * SetTempNamespaceState - set status of session's temporary namespace
+ *
+ * This is used for conveying state to a parallel worker, and is not meant for
+ * general-purpose access.  By transferring these namespace OIDs to workers,
+ * we ensure they will have the same notion of the search path as their leader
+ * does.
+ */
+void
+SetTempNamespaceState(Oid tempNamespaceId, Oid tempToastNamespaceId)
+{
+	/* Worker should not have created its own namespaces ... */
+	Assert(myTempNamespace == InvalidOid);
+	Assert(myTempToastNamespace == InvalidOid);
+	Assert(myTempNamespaceSubID == InvalidSubTransactionId);
+
+	/* Assign same namespace OIDs that leader has */
+	myTempNamespace = tempNamespaceId;
+	myTempToastNamespace = tempToastNamespaceId;
+
+	/*
+	 * It's fine to leave myTempNamespaceSubID == InvalidSubTransactionId.
+	 * Even if the namespace is new so far as the leader is concerned, it's
+	 * not new to the worker, and we certainly wouldn't want the worker trying
+	 * to destroy it.
+	 */
+
+	baseSearchPathValid = false;	/* may need to rebuild list */
+}
+
+
 /*
  * GetOverrideSearchPath - fetch current search path definition in form
  * used by PushOverrideSearchPath.
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index fe68c998e8de130c0fb446e22a4090f5c7aca66b..67f1906326489afe89c63ecc521913da1dd24a52 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -85,7 +85,7 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 	switch (relpersistence)
 	{
 		case RELPERSISTENCE_TEMP:
-			backend = MyBackendId;
+			backend = BackendIdForTempRelations();
 			needs_wal = false;
 			break;
 		case RELPERSISTENCE_UNLOGGED:
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index e7909eb5d5958532f5e8b5b82544ec4ef227d218..dc814a2b3ac8eb44ed62a4837592c42a449461a5 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -115,7 +115,6 @@ static bool has_parallel_hazard_walker(Node *node,
 						   has_parallel_hazard_arg *context);
 static bool parallel_too_dangerous(char proparallel,
 					   has_parallel_hazard_arg *context);
-static bool typeid_is_temp(Oid typeid);
 static bool contain_nonstrict_functions_walker(Node *node, void *context);
 static bool contain_leaked_vars_walker(Node *node, void *context);
 static Relids find_nonnullable_rels_walker(Node *node, bool top_level);
@@ -1409,49 +1408,6 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
 		return has_parallel_hazard_walker((Node *) rinfo->clause, context);
 	}
 
-	/*
-	 * It is an error for a parallel worker to touch a temporary table in any
-	 * way, so we can't handle nodes whose type is the rowtype of such a
-	 * table.
-	 */
-	if (!context->allow_restricted)
-	{
-		switch (nodeTag(node))
-		{
-			case T_Var:
-			case T_Const:
-			case T_Param:
-			case T_Aggref:
-			case T_WindowFunc:
-			case T_ArrayRef:
-			case T_FuncExpr:
-			case T_NamedArgExpr:
-			case T_OpExpr:
-			case T_DistinctExpr:
-			case T_NullIfExpr:
-			case T_FieldSelect:
-			case T_FieldStore:
-			case T_RelabelType:
-			case T_CoerceViaIO:
-			case T_ArrayCoerceExpr:
-			case T_ConvertRowtypeExpr:
-			case T_CaseExpr:
-			case T_CaseTestExpr:
-			case T_ArrayExpr:
-			case T_RowExpr:
-			case T_CoalesceExpr:
-			case T_MinMaxExpr:
-			case T_CoerceToDomain:
-			case T_CoerceToDomainValue:
-			case T_SetToDefault:
-				if (typeid_is_temp(exprType(node)))
-					return true;
-				break;
-			default:
-				break;
-		}
-	}
-
 	/*
 	 * For each node that might potentially call a function, we need to
 	 * examine the pg_proc.proparallel marking for that function to see
@@ -1558,17 +1514,6 @@ parallel_too_dangerous(char proparallel, has_parallel_hazard_arg *context)
 		return proparallel != PROPARALLEL_SAFE;
 }
 
-static bool
-typeid_is_temp(Oid typeid)
-{
-	Oid			relid = get_typ_typrelid(typeid);
-
-	if (!OidIsValid(relid))
-		return false;
-
-	return (get_rel_persistence(relid) == RELPERSISTENCE_TEMP);
-}
-
 /*****************************************************************************
  *		Check clauses for nonstrict functions
  *****************************************************************************/
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 201ce2668fd4fe1d1e40431f12ced6574b9523d8..53981794b98c7b803a5cbe8c8ad39d9b90008f8c 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -15,6 +15,7 @@
  */
 #include "postgres.h"
 
+#include "access/parallel.h"
 #include "catalog/catalog.h"
 #include "executor/instrument.h"
 #include "storage/buf_internals.h"
@@ -412,6 +413,19 @@ InitLocalBuffers(void)
 	HASHCTL		info;
 	int			i;
 
+	/*
+	 * Parallel workers can't access data in temporary tables, because they
+	 * have no visibility into the local buffers of their leader.  This is a
+	 * convenient, low-cost place to provide a backstop check for that.  Note
+	 * that we don't wish to prevent a parallel worker from accessing catalog
+	 * metadata about a temp table, so checks at higher levels would be
+	 * inappropriate.
+	 */
+	if (IsParallelWorker())
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
+				 errmsg("cannot access temporary tables during a parallel operation")));
+
 	/* Allocate and zero buffer headers and auxiliary arrays */
 	LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
 	LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 4ec2fed2b354232d3d66cb830f0e21507c40d1a7..0776f3bf6cf6c0b825bc80c7a437691f72e6839a 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -1004,7 +1004,7 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 			break;
 		case RELPERSISTENCE_TEMP:
 			if (isTempOrTempToastNamespace(relform->relnamespace))
-				backend = MyBackendId;
+				backend = BackendIdForTempRelations();
 			else
 			{
 				/* Do it the hard way. */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index afb6c8772da1627187b795ee83701a3736763e61..70a651a8fc322115cef14010d63bad4cf0876db8 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -993,7 +993,7 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 		case RELPERSISTENCE_TEMP:
 			if (isTempOrTempToastNamespace(relation->rd_rel->relnamespace))
 			{
-				relation->rd_backend = MyBackendId;
+				relation->rd_backend = BackendIdForTempRelations();
 				relation->rd_islocaltemp = true;
 			}
 			else
@@ -2970,7 +2970,7 @@ RelationBuildLocalRelation(const char *relname,
 			break;
 		case RELPERSISTENCE_TEMP:
 			Assert(isTempOrTempToastNamespace(relnamespace));
-			rel->rd_backend = MyBackendId;
+			rel->rd_backend = BackendIdForTempRelations();
 			rel->rd_islocaltemp = true;
 			break;
 		default:
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 5a7783b981e94060a3971831a21104b90e94bbfb..f23208353c340137700e19fbc37804136d3fdc0e 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -71,6 +71,8 @@ char		postgres_exec_path[MAXPGPATH];		/* full path to backend */
 
 BackendId	MyBackendId = InvalidBackendId;
 
+BackendId	ParallelMasterBackendId = InvalidBackendId;
+
 Oid			MyDatabaseId = InvalidOid;
 
 Oid			MyDatabaseTableSpace = InvalidOid;
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 2ccb3a7a03ad056669b4a070ad0e78191b0a96c7..eee94d862267440c89c7d296805c14bc5b41a042 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -125,6 +125,10 @@ extern bool isAnyTempNamespace(Oid namespaceId);
 extern bool isOtherTempNamespace(Oid namespaceId);
 extern int	GetTempNamespaceBackendId(Oid namespaceId);
 extern Oid	GetTempToastNamespace(void);
+extern void GetTempNamespaceState(Oid *tempNamespaceId,
+					  Oid *tempToastNamespaceId);
+extern void SetTempNamespaceState(Oid tempNamespaceId,
+					  Oid tempToastNamespaceId);
 extern void ResetTempTableNamespace(void);
 
 extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
diff --git a/src/include/storage/backendid.h b/src/include/storage/backendid.h
index 7d9676f5348be0435b6394322a7a5e7e394cdffe..dec8a97271506d9724b5d3923ac71dc4b78ece0f 100644
--- a/src/include/storage/backendid.h
+++ b/src/include/storage/backendid.h
@@ -24,4 +24,14 @@ typedef int BackendId;			/* unique currently active backend identifier */
 
 extern PGDLLIMPORT BackendId MyBackendId;		/* backend id of this backend */
 
+/* backend id of our parallel session leader, or InvalidBackendId if none */
+extern PGDLLIMPORT BackendId ParallelMasterBackendId;
+
+/*
+ * The BackendId to use for our session's temp relations is normally our own,
+ * but parallel workers should use their leader's ID.
+ */
+#define BackendIdForTempRelations() \
+	(ParallelMasterBackendId == InvalidBackendId ? MyBackendId : ParallelMasterBackendId)
+
 #endif   /* BACKENDID_H */