diff --git a/src/backend/access/gin/ginbtree.c b/src/backend/access/gin/ginbtree.c
index 070cd9209e129a913bddd06476c78cd4e3afc941..9d857a0310234b1ee6af3dfe702b2dc3748ebb4a 100644
--- a/src/backend/access/gin/ginbtree.c
+++ b/src/backend/access/gin/ginbtree.c
@@ -304,7 +304,7 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats)
 
 			MarkBufferDirty(stack->buffer);
 
-			if (!btree->index->rd_istemp)
+			if (RelationNeedsWAL(btree->index))
 			{
 				XLogRecPtr	recptr;
 
@@ -373,7 +373,7 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats)
 				MarkBufferDirty(lbuffer);
 				MarkBufferDirty(stack->buffer);
 
-				if (!btree->index->rd_istemp)
+				if (RelationNeedsWAL(btree->index))
 				{
 					XLogRecPtr	recptr;
 
@@ -422,7 +422,7 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats)
 				MarkBufferDirty(rbuffer);
 				MarkBufferDirty(stack->buffer);
 
-				if (!btree->index->rd_istemp)
+				if (RelationNeedsWAL(btree->index))
 				{
 					XLogRecPtr	recptr;
 
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index 525f79cea7adc55a5009e0a8178df5e9fb296ab6..74339c9eeafd874156b5dbaf42382e5ceef3fd1e 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -103,7 +103,7 @@ writeListPage(Relation index, Buffer buffer,
 
 	MarkBufferDirty(buffer);
 
-	if (!index->rd_istemp)
+	if (RelationNeedsWAL(index))
 	{
 		XLogRecData rdata[2];
 		ginxlogInsertListPage data;
@@ -384,7 +384,7 @@ ginHeapTupleFastInsert(Relation index, GinState *ginstate,
 	 */
 	MarkBufferDirty(metabuffer);
 
-	if (!index->rd_istemp)
+	if (RelationNeedsWAL(index))
 	{
 		XLogRecPtr	recptr;
 
@@ -564,7 +564,7 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
 			MarkBufferDirty(buffers[i]);
 		}
 
-		if (!index->rd_istemp)
+		if (RelationNeedsWAL(index))
 		{
 			XLogRecPtr	recptr;
 
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index fa70e4fa55d80083e1f33a73eaa2d1902c2f5b6b..8681edefe673a29b287c844357e55412113c77ce 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -55,7 +55,7 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
 
 	MarkBufferDirty(buffer);
 
-	if (!index->rd_istemp)
+	if (RelationNeedsWAL(index))
 	{
 		XLogRecPtr	recptr;
 		XLogRecData rdata[2];
@@ -325,7 +325,7 @@ ginbuild(PG_FUNCTION_ARGS)
 	GinInitBuffer(RootBuffer, GIN_LEAF);
 	MarkBufferDirty(RootBuffer);
 
-	if (!index->rd_istemp)
+	if (RelationNeedsWAL(index))
 	{
 		XLogRecPtr	recptr;
 		XLogRecData rdata;
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 27326acec9ae9139cf0a459e432509609f1c459b..5f20ac9349f04ad230315107635276035bdb8f80 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -410,7 +410,7 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
 
 	MarkBufferDirty(metabuffer);
 
-	if (!index->rd_istemp)
+	if (RelationNeedsWAL(index))
 	{
 		XLogRecPtr			recptr;
 		ginxlogUpdateMeta	data;
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index 7dfecffc877f91177b6bb2cabb92f881a4c9f93e..4b35acb983aad548ae98c170de09a07aac11a145 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -93,7 +93,7 @@ xlogVacuumPage(Relation index, Buffer buffer)
 
 	Assert(GinPageIsLeaf(page));
 
-	if (index->rd_istemp)
+	if (!RelationNeedsWAL(index))
 		return;
 
 	data.node = index->rd_node;
@@ -308,7 +308,7 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn
 		MarkBufferDirty(lBuffer);
 	MarkBufferDirty(dBuffer);
 
-	if (!gvs->index->rd_istemp)
+	if (RelationNeedsWAL(gvs->index))
 	{
 		XLogRecPtr	recptr;
 		XLogRecData rdata[4];
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index d6aaea2162d3d79d19fb2e8903ffd9ccc30c0374..b34830bb4247933cfc9da86a173e0079000378f1 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -115,7 +115,7 @@ gistbuild(PG_FUNCTION_ARGS)
 
 	MarkBufferDirty(buffer);
 
-	if (!index->rd_istemp)
+	if (RelationNeedsWAL(index))
 	{
 		XLogRecPtr	recptr;
 		XLogRecData rdata;
@@ -401,7 +401,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
 			dist->page = BufferGetPage(dist->buffer);
 		}
 
-		if (!state->r->rd_istemp)
+		if (RelationNeedsWAL(state->r))
 		{
 			XLogRecPtr	recptr;
 			XLogRecData *rdata;
@@ -465,7 +465,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
 
 		MarkBufferDirty(state->stack->buffer);
 
-		if (!state->r->rd_istemp)
+		if (RelationNeedsWAL(state->r))
 		{
 			OffsetNumber noffs = 0,
 						offs[1];
@@ -550,7 +550,7 @@ gistfindleaf(GISTInsertState *state, GISTSTATE *giststate)
 		opaque = GistPageGetOpaque(state->stack->page);
 
 		state->stack->lsn = PageGetLSN(state->stack->page);
-		Assert(state->r->rd_istemp || !XLogRecPtrIsInvalid(state->stack->lsn));
+		Assert(!RelationNeedsWAL(state->r) || !XLogRecPtrIsInvalid(state->stack->lsn));
 
 		if (state->stack->blkno != GIST_ROOT_BLKNO &&
 			XLByteLT(state->stack->parent->lsn, opaque->nsn))
@@ -911,7 +911,7 @@ gistmakedeal(GISTInsertState *state, GISTSTATE *giststate)
 	}
 
 	/* say to xlog that insert is completed */
-	if (state->needInsertComplete && !state->r->rd_istemp)
+	if (state->needInsertComplete && RelationNeedsWAL(state->r))
 		gistxlogInsertCompletion(state->r->rd_node, &(state->key), 1);
 }
 
@@ -1011,7 +1011,7 @@ gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer ke
 
 	MarkBufferDirty(buffer);
 
-	if (!r->rd_istemp)
+	if (RelationNeedsWAL(r))
 	{
 		XLogRecPtr	recptr;
 		XLogRecData *rdata;
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index dbe9406ceb8b6bc53d6bf03faa916571a6ca5e6e..e02e72d4313829c7c15f3710fe68b2ab84340bf0 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -248,7 +248,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
 					PageIndexTupleDelete(page, todelete[i]);
 				GistMarkTuplesDeleted(page);
 
-				if (!rel->rd_istemp)
+				if (RelationNeedsWAL(rel))
 				{
 					XLogRecData *rdata;
 					XLogRecPtr	recptr;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 4fe3a7391090d6d3a6826d4601f04c603c21718b..4020906b347629a17aa3a7c4af947b3e0fa70e11 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -124,7 +124,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool is_rescan)
 	 *
 	 * During a rescan, don't make a new strategy object if we don't have to.
 	 */
-	if (!scan->rs_rd->rd_istemp &&
+	if (!RelationUsesLocalBuffers(scan->rs_rd) &&
 		scan->rs_nblocks > NBuffers / 4)
 	{
 		allow_strat = scan->rs_allow_strat;
@@ -905,7 +905,7 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 		elog(ERROR, "could not open relation with OID %u", relationId);
 
 	/* Make note that we've accessed a temporary relation */
-	if (r->rd_istemp)
+	if (RelationUsesLocalBuffers(r))
 		MyXactAccessedTempRel = true;
 
 	pgstat_initstats(r);
@@ -951,7 +951,7 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 		elog(ERROR, "could not open relation with OID %u", relationId);
 
 	/* Make note that we've accessed a temporary relation */
-	if (r->rd_istemp)
+	if (RelationUsesLocalBuffers(r))
 		MyXactAccessedTempRel = true;
 
 	pgstat_initstats(r);
@@ -1917,7 +1917,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
 	MarkBufferDirty(buffer);
 
 	/* XLOG stuff */
-	if (!(options & HEAP_INSERT_SKIP_WAL) && !relation->rd_istemp)
+	if (!(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation))
 	{
 		xl_heap_insert xlrec;
 		xl_heap_header xlhdr;
@@ -2227,7 +2227,7 @@ l1:
 	MarkBufferDirty(buffer);
 
 	/* XLOG stuff */
-	if (!relation->rd_istemp)
+	if (RelationNeedsWAL(relation))
 	{
 		xl_heap_delete xlrec;
 		XLogRecPtr	recptr;
@@ -2780,7 +2780,7 @@ l2:
 	MarkBufferDirty(buffer);
 
 	/* XLOG stuff */
-	if (!relation->rd_istemp)
+	if (RelationNeedsWAL(relation))
 	{
 		XLogRecPtr	recptr = log_heap_update(relation, buffer, oldtup.t_self,
 											 newbuf, heaptup,
@@ -3403,7 +3403,7 @@ l3:
 	 * (Also, in a PITR log-shipping or 2PC environment, we have to have XLOG
 	 * entries for everything anyway.)
 	 */
-	if (!relation->rd_istemp)
+	if (RelationNeedsWAL(relation))
 	{
 		xl_heap_lock xlrec;
 		XLogRecPtr	recptr;
@@ -3505,7 +3505,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
 	MarkBufferDirty(buffer);
 
 	/* XLOG stuff */
-	if (!relation->rd_istemp)
+	if (RelationNeedsWAL(relation))
 	{
 		xl_heap_inplace xlrec;
 		XLogRecPtr	recptr;
@@ -3867,8 +3867,8 @@ log_heap_clean(Relation reln, Buffer buffer,
 	XLogRecPtr	recptr;
 	XLogRecData rdata[4];
 
-	/* Caller should not call me on a temp relation */
-	Assert(!reln->rd_istemp);
+	/* Caller should not call me on a non-WAL-logged relation */
+	Assert(RelationNeedsWAL(reln));
 
 	xlrec.node = reln->rd_node;
 	xlrec.block = BufferGetBlockNumber(buffer);
@@ -3950,8 +3950,8 @@ log_heap_freeze(Relation reln, Buffer buffer,
 	XLogRecPtr	recptr;
 	XLogRecData rdata[2];
 
-	/* Caller should not call me on a temp relation */
-	Assert(!reln->rd_istemp);
+	/* Caller should not call me on a non-WAL-logged relation */
+	Assert(RelationNeedsWAL(reln));
 	/* nor when there are no tuples to freeze */
 	Assert(offcnt > 0);
 
@@ -3996,8 +3996,8 @@ log_heap_update(Relation reln, Buffer oldbuf, ItemPointerData from,
 	XLogRecData rdata[4];
 	Page		page = BufferGetPage(newbuf);
 
-	/* Caller should not call me on a temp relation */
-	Assert(!reln->rd_istemp);
+	/* Caller should not call me on a non-WAL-logged relation */
+	Assert(RelationNeedsWAL(reln));
 
 	if (HeapTupleIsHeapOnly(newtup))
 		info = XLOG_HEAP_HOT_UPDATE;
@@ -4997,7 +4997,7 @@ heap2_desc(StringInfo buf, uint8 xl_info, char *rec)
  *	heap_sync		- sync a heap, for use when no WAL has been written
  *
  * This forces the heap contents (including TOAST heap if any) down to disk.
- * If we skipped using WAL, and it's not a temp relation, we must force the
+ * If we skipped using WAL, and WAL is otherwise needed, we must force the
  * relation down to disk before it's safe to commit the transaction.  This
  * requires writing out any dirty buffers and then doing a forced fsync.
  *
@@ -5010,8 +5010,8 @@ heap2_desc(StringInfo buf, uint8 xl_info, char *rec)
 void
 heap_sync(Relation rel)
 {
-	/* temp tables never need fsync */
-	if (rel->rd_istemp)
+	/* non-WAL-logged tables never need fsync */
+	if (!RelationNeedsWAL(rel))
 		return;
 
 	/* main heap */
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index ee5f38fccd65abb68e192f605201f145e77cb537..d1b08b3a8bc232252498bc18ad9c07745d9206b8 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -233,7 +233,7 @@ heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin,
 		/*
 		 * Emit a WAL HEAP_CLEAN record showing what we did
 		 */
-		if (!relation->rd_istemp)
+		if (RelationNeedsWAL(relation))
 		{
 			XLogRecPtr	recptr;
 
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 19ca302ebb9456c1ce9d767dd0659ac11f6b1678..eb2dbffb9dd0dee7f72338431cbc1d88706ac249 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -277,8 +277,8 @@ end_heap_rewrite(RewriteState state)
 	}
 
 	/*
-	 * If the rel isn't temp, must fsync before commit.  We use heap_sync to
-	 * ensure that the toast table gets fsync'd too.
+	 * If the rel is WAL-logged, must fsync before commit.  We use heap_sync
+	 * to ensure that the toast table gets fsync'd too.
 	 *
 	 * It's obvious that we must do this when not WAL-logging. It's less
 	 * obvious that we have to do it even if we did WAL-log the pages. The
@@ -287,7 +287,7 @@ end_heap_rewrite(RewriteState state)
 	 * occurring during the rewriteheap operation won't have fsync'd data we
 	 * wrote before the checkpoint.
 	 */
-	if (!state->rs_new_rel->rd_istemp)
+	if (RelationNeedsWAL(state->rs_new_rel))
 		heap_sync(state->rs_new_rel);
 
 	/* Deleting the context frees everything */
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index eaad8122b11cfaeb2ecb9e41b6949ed904d1cdb0..ee0f04cdb5b63f492767eb95c5f9f05662977db4 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -766,7 +766,7 @@ _bt_insertonpg(Relation rel,
 		}
 
 		/* XLOG stuff */
-		if (!rel->rd_istemp)
+		if (RelationNeedsWAL(rel))
 		{
 			xl_btree_insert xlrec;
 			BlockNumber xldownlink;
@@ -1165,7 +1165,7 @@ _bt_split(Relation rel, Buffer buf, OffsetNumber firstright,
 	}
 
 	/* XLOG stuff */
-	if (!rel->rd_istemp)
+	if (RelationNeedsWAL(rel))
 	{
 		xl_btree_split xlrec;
 		uint8		xlinfo;
@@ -1914,7 +1914,7 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
 	MarkBufferDirty(metabuf);
 
 	/* XLOG stuff */
-	if (!rel->rd_istemp)
+	if (RelationNeedsWAL(rel))
 	{
 		xl_btree_newroot xlrec;
 		XLogRecPtr	recptr;
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index e0c0f21f4efaa7821c917c1b714d3add448af060..2b4478089d81f20057530d9041501ecac2135ffe 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -224,7 +224,7 @@ _bt_getroot(Relation rel, int access)
 		MarkBufferDirty(metabuf);
 
 		/* XLOG stuff */
-		if (!rel->rd_istemp)
+		if (RelationNeedsWAL(rel))
 		{
 			xl_btree_newroot xlrec;
 			XLogRecPtr	recptr;
@@ -452,7 +452,7 @@ _bt_checkpage(Relation rel, Buffer buf)
 static void
 _bt_log_reuse_page(Relation rel, BlockNumber blkno, TransactionId latestRemovedXid)
 {
-	if (rel->rd_istemp)
+	if (!RelationNeedsWAL(rel))
 		return;
 
 	/* No ereport(ERROR) until changes are logged */
@@ -751,7 +751,7 @@ _bt_delitems_vacuum(Relation rel, Buffer buf,
 	MarkBufferDirty(buf);
 
 	/* XLOG stuff */
-	if (!rel->rd_istemp)
+	if (RelationNeedsWAL(rel))
 	{
 		XLogRecPtr	recptr;
 		XLogRecData rdata[2];
@@ -829,7 +829,7 @@ _bt_delitems_delete(Relation rel, Buffer buf,
 	MarkBufferDirty(buf);
 
 	/* XLOG stuff */
-	if (!rel->rd_istemp)
+	if (RelationNeedsWAL(rel))
 	{
 		XLogRecPtr	recptr;
 		XLogRecData rdata[3];
@@ -1365,7 +1365,7 @@ _bt_pagedel(Relation rel, Buffer buf, BTStack stack)
 		MarkBufferDirty(lbuf);
 
 	/* XLOG stuff */
-	if (!rel->rd_istemp)
+	if (RelationNeedsWAL(rel))
 	{
 		xl_btree_delete_page xlrec;
 		xl_btree_metadata xlmeta;
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index a1d3aef353ef953c92d31191fac8a8d03282ddba..3fb43a2e588377b1f5ce01637f2fec1a02c34b9f 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -211,9 +211,9 @@ _bt_leafbuild(BTSpool *btspool, BTSpool *btspool2)
 
 	/*
 	 * We need to log index creation in WAL iff WAL archiving/streaming is
-	 * enabled AND it's not a temp index.
+	 * enabled UNLESS the index isn't WAL-logged anyway.
 	 */
-	wstate.btws_use_wal = XLogIsNeeded() && !wstate.index->rd_istemp;
+	wstate.btws_use_wal = XLogIsNeeded() && RelationNeedsWAL(wstate.index);
 
 	/* reserve the metapage */
 	wstate.btws_pages_alloced = BTREE_METAPAGE + 1;
@@ -797,9 +797,9 @@ _bt_load(BTWriteState *wstate, BTSpool *btspool, BTSpool *btspool2)
 	_bt_uppershutdown(wstate, state);
 
 	/*
-	 * If the index isn't temp, we must fsync it down to disk before it's safe
-	 * to commit the transaction.  (For a temp index we don't care since the
-	 * index will be uninteresting after a crash anyway.)
+	 * If the index is WAL-logged, we must fsync it down to disk before it's
+	 * safe to commit the transaction.  (For a non-WAL-logged index we don't
+	 * care since the index will be uninteresting after a crash anyway.)
 	 *
 	 * It's obvious that we must do this when not WAL-logging the build. It's
 	 * less obvious that we have to do it even if we did WAL-log the index
@@ -811,7 +811,7 @@ _bt_load(BTWriteState *wstate, BTSpool *btspool, BTSpool *btspool2)
 	 * fsync those pages here, they might still not be on disk when the crash
 	 * occurs.
 	 */
-	if (!wstate->index->rd_istemp)
+	if (RelationNeedsWAL(wstate->index))
 	{
 		RelationOpenSmgr(wstate->index);
 		smgrimmedsync(wstate->index->rd_smgr, MAIN_FORKNUM);
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index e475403b9e56f34a09be3ec237a3e2b72565c261..73ef114d9ccc541a81427731ff231e9552d3e08e 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -219,6 +219,7 @@ Boot_CreateStmt:
 												   $3,
 												   tupdesc,
 												   RELKIND_RELATION,
+												   RELPERSISTENCE_PERMANENT,
 												   shared_relation,
 												   mapped_relation,
 												   true);
@@ -238,6 +239,7 @@ Boot_CreateStmt:
 													  tupdesc,
 													  NIL,
 													  RELKIND_RELATION,
+													  RELPERSISTENCE_PERMANENT,
 													  shared_relation,
 													  mapped_relation,
 													  true,
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 63225127f7a537b8ac6f9b0489066492f62351f8..88b5c2a215d10827df0406cb53cd18e9b199332f 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -524,12 +524,26 @@ GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
  * created by bootstrap have preassigned OIDs, so there's no need.
  */
 Oid
-GetNewRelFileNode(Oid reltablespace, Relation pg_class, BackendId backend)
+GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 {
 	RelFileNodeBackend rnode;
 	char	   *rpath;
 	int			fd;
 	bool		collides;
+	BackendId	backend;
+
+	switch (relpersistence)
+	{
+		case RELPERSISTENCE_TEMP:
+			backend = MyBackendId;
+			break;
+		case RELPERSISTENCE_PERMANENT:
+			backend = InvalidBackendId;
+			break;
+		default:
+			elog(ERROR, "invalid relpersistence: %c", relpersistence);
+			return InvalidOid;	/* placate compiler */
+	}
 
 	/* This logic should match RelationInitPhysicalAddr */
 	rnode.node.spcNode = reltablespace ? reltablespace : MyDatabaseTableSpace;
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 9b7668c133ef8dba82450e74ef37d9a13ea11061..bcf6caa2eef325c82c8d5f4160ecd37908fc3e3b 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -238,6 +238,7 @@ heap_create(const char *relname,
 			Oid relid,
 			TupleDesc tupDesc,
 			char relkind,
+			char relpersistence,
 			bool shared_relation,
 			bool mapped_relation,
 			bool allow_system_table_mods)
@@ -311,7 +312,8 @@ heap_create(const char *relname,
 									 relid,
 									 reltablespace,
 									 shared_relation,
-									 mapped_relation);
+									 mapped_relation,
+									 relpersistence);
 
 	/*
 	 * Have the storage manager create the relation's disk file, if needed.
@@ -322,7 +324,7 @@ heap_create(const char *relname,
 	if (create_storage)
 	{
 		RelationOpenSmgr(rel);
-		RelationCreateStorage(rel->rd_node, rel->rd_istemp);
+		RelationCreateStorage(rel->rd_node, relpersistence);
 	}
 
 	return rel;
@@ -693,7 +695,7 @@ InsertPgClassTuple(Relation pg_class_desc,
 	values[Anum_pg_class_reltoastidxid - 1] = ObjectIdGetDatum(rd_rel->reltoastidxid);
 	values[Anum_pg_class_relhasindex - 1] = BoolGetDatum(rd_rel->relhasindex);
 	values[Anum_pg_class_relisshared - 1] = BoolGetDatum(rd_rel->relisshared);
-	values[Anum_pg_class_relistemp - 1] = BoolGetDatum(rd_rel->relistemp);
+	values[Anum_pg_class_relpersistence - 1] = CharGetDatum(rd_rel->relpersistence);
 	values[Anum_pg_class_relkind - 1] = CharGetDatum(rd_rel->relkind);
 	values[Anum_pg_class_relnatts - 1] = Int16GetDatum(rd_rel->relnatts);
 	values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks);
@@ -898,6 +900,7 @@ heap_create_with_catalog(const char *relname,
 						 TupleDesc tupdesc,
 						 List *cooked_constraints,
 						 char relkind,
+						 char relpersistence,
 						 bool shared_relation,
 						 bool mapped_relation,
 						 bool oidislocal,
@@ -997,8 +1000,7 @@ heap_create_with_catalog(const char *relname,
 		}
 		else
 			relid = GetNewRelFileNode(reltablespace, pg_class_desc,
-									  isTempOrToastNamespace(relnamespace) ?
-										  MyBackendId : InvalidBackendId);
+									  relpersistence);
 	}
 
 	/*
@@ -1036,6 +1038,7 @@ heap_create_with_catalog(const char *relname,
 							   relid,
 							   tupdesc,
 							   relkind,
+							   relpersistence,
 							   shared_relation,
 							   mapped_relation,
 							   allow_system_table_mods);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index b437c9976a1ea9dc17bda274cdc5b2b73d0e23b1..8fbe8ebc91d4e27a8c07753701263e41bd6284c8 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -545,6 +545,7 @@ index_create(Oid heapRelationId,
 	bool		is_exclusion;
 	Oid			namespaceId;
 	int			i;
+	char		relpersistence;
 
 	is_exclusion = (indexInfo->ii_ExclusionOps != NULL);
 
@@ -561,11 +562,13 @@ index_create(Oid heapRelationId,
 	/*
 	 * The index will be in the same namespace as its parent table, and is
 	 * shared across databases if and only if the parent is.  Likewise, it
-	 * will use the relfilenode map if and only if the parent does.
+	 * will use the relfilenode map if and only if the parent does; and it
+	 * inherits the parent's relpersistence.
 	 */
 	namespaceId = RelationGetNamespace(heapRelation);
 	shared_relation = heapRelation->rd_rel->relisshared;
 	mapped_relation = RelationIsMapped(heapRelation);
+	relpersistence = heapRelation->rd_rel->relpersistence;
 
 	/*
 	 * check parameters
@@ -646,9 +649,7 @@ index_create(Oid heapRelationId,
 		else
 		{
 			indexRelationId =
-				GetNewRelFileNode(tableSpaceId, pg_class,
-								  heapRelation->rd_istemp ?
-									MyBackendId : InvalidBackendId);
+				GetNewRelFileNode(tableSpaceId, pg_class, relpersistence);
 		}
 	}
 
@@ -663,6 +664,7 @@ index_create(Oid heapRelationId,
 								indexRelationId,
 								indexTupDesc,
 								RELKIND_INDEX,
+								relpersistence,
 								shared_relation,
 								mapped_relation,
 								allow_system_table_mods);
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 653c9ada118c061e4d2cce168aca6ccc92499c6f..84cbfeb0a7d083ae714688825eb571de46181e9b 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -235,14 +235,14 @@ RangeVarGetRelid(const RangeVar *relation, bool failOK)
 	}
 
 	/*
-	 * If istemp is set, this is a reference to a temp relation.  The parser
-	 * never generates such a RangeVar in simple DML, but it can happen in
-	 * contexts such as "CREATE TEMP TABLE foo (f1 int PRIMARY KEY)".  Such a
-	 * command will generate an added CREATE INDEX operation, which must be
+	 * Some non-default relpersistence value may have been specified.  The
+	 * parser never generates such a RangeVar in simple DML, but it can happen
+	 * in contexts such as "CREATE TEMP TABLE foo (f1 int PRIMARY KEY)".  Such
+	 * a command will generate an added CREATE INDEX operation, which must be
 	 * careful to find the temp table, even when pg_temp is not first in the
 	 * search path.
 	 */
-	if (relation->istemp)
+	if (relation->relpersistence == RELPERSISTENCE_TEMP)
 	{
 		if (relation->schemaname)
 			ereport(ERROR,
@@ -308,7 +308,7 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation)
 							newRelation->relname)));
 	}
 
-	if (newRelation->istemp)
+	if (newRelation->relpersistence == RELPERSISTENCE_TEMP)
 	{
 		/* TEMP tables are created in our backend-local temp namespace */
 		if (newRelation->schemaname)
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 0ce2051b6b148d879825556b4a933fef89a63883..671aaff133a25a3969604081d0b1570de4d02988 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -95,19 +95,35 @@ typedef struct xl_smgr_truncate
  * transaction aborts later on, the storage will be destroyed.
  */
 void
-RelationCreateStorage(RelFileNode rnode, bool istemp)
+RelationCreateStorage(RelFileNode rnode, char relpersistence)
 {
 	PendingRelDelete *pending;
 	XLogRecPtr	lsn;
 	XLogRecData rdata;
 	xl_smgr_create xlrec;
 	SMgrRelation srel;
-	BackendId	backend = istemp ? MyBackendId : InvalidBackendId;
+	BackendId	backend;
+	bool		needs_wal;
+
+	switch (relpersistence)
+	{
+		case RELPERSISTENCE_TEMP:
+			backend = MyBackendId;
+			needs_wal = false;
+			break;
+		case RELPERSISTENCE_PERMANENT:
+			backend = InvalidBackendId;
+			needs_wal = true;
+			break;
+		default:
+			elog(ERROR, "invalid relpersistence: %c", relpersistence);
+			return;			/* placate compiler */
+	}
 
 	srel = smgropen(rnode, backend);
 	smgrcreate(srel, MAIN_FORKNUM, false);
 
-	if (!istemp)
+	if (needs_wal)
 	{
 		/*
 		 * Make an XLOG entry reporting the file creation.
@@ -253,7 +269,7 @@ RelationTruncate(Relation rel, BlockNumber nblocks)
 	 * failure to truncate, that might spell trouble at WAL replay, into a
 	 * certain PANIC.
 	 */
-	if (!rel->rd_istemp)
+	if (RelationNeedsWAL(rel))
 	{
 		/*
 		 * Make an XLOG entry reporting the file truncation.
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 7bf64e22ee8c57ba5df32d4a1d21a9ae89c8581f..d1f6c9f36836c12076a9d3a8c536b2dddc220cfd 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -195,7 +195,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
 	 * Toast tables for regular relations go in pg_toast; those for temp
 	 * relations go into the per-backend temp-toast-table namespace.
 	 */
-	if (rel->rd_backend == MyBackendId)
+	if (RelationUsesTempNamespace(rel))
 		namespaceid = GetTempToastNamespace();
 	else
 		namespaceid = PG_TOAST_NAMESPACE;
@@ -216,6 +216,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
 										   tupdesc,
 										   NIL,
 										   RELKIND_TOASTVALUE,
+										   rel->rd_rel->relpersistence,
 										   shared_relation,
 										   mapped_relation,
 										   true,
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index e1dbd6d985bd88496a27b76c86488493c8d0f61d..249067f1e2d0b7a01d781831fae1ed4c1e2efcbb 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -675,6 +675,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
 										  tupdesc,
 										  NIL,
 										  OldHeap->rd_rel->relkind,
+										  OldHeap->rd_rel->relpersistence,
 										  false,
 										  RelationIsMapped(OldHeap),
 										  true,
@@ -789,9 +790,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
 
 	/*
 	 * We need to log the copied data in WAL iff WAL archiving/streaming is
-	 * enabled AND it's not a temp rel.
+	 * enabled AND it's not a WAL-logged rel.
 	 */
-	use_wal = XLogIsNeeded() && !NewHeap->rd_istemp;
+	use_wal = XLogIsNeeded() && RelationNeedsWAL(NewHeap);
 
 	/* use_wal off requires smgr_targblock be initially invalid */
 	Assert(RelationGetTargetBlock(NewHeap) == InvalidBlockNumber);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 9407d0f02d799df443161e93b83514bde8403842..0940893135371626a9c91f2799e36371ad412b70 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -222,7 +222,7 @@ DefineIndex(RangeVar *heapRelation,
 	}
 	else
 	{
-		tablespaceId = GetDefaultTablespace(rel->rd_istemp);
+		tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence);
 		/* note InvalidOid is OK in this case */
 	}
 
@@ -1706,7 +1706,7 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
 			continue;
 
 		/* Skip temp tables of other backends; we can't reindex them at all */
-		if (classtuple->relistemp &&
+		if (classtuple->relpersistence == RELPERSISTENCE_TEMP &&
 			!isTempNamespace(classtuple->relnamespace))
 			continue;
 
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index bb8ebce25a0b4f833526d289d42586c160507b31..e1df5fb13c7dbdb39b09734312df9879d23021f0 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -366,7 +366,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 	MarkBufferDirty(buf);
 
 	/* XLOG stuff */
-	if (!rel->rd_istemp)
+	if (RelationNeedsWAL(rel))
 	{
 		xl_seq_rec	xlrec;
 		XLogRecPtr	recptr;
@@ -448,7 +448,7 @@ AlterSequence(AlterSeqStmt *stmt)
 	MarkBufferDirty(buf);
 
 	/* XLOG stuff */
-	if (!seqrel->rd_istemp)
+	if (RelationNeedsWAL(seqrel))
 	{
 		xl_seq_rec	xlrec;
 		XLogRecPtr	recptr;
@@ -678,7 +678,7 @@ nextval_internal(Oid relid)
 	MarkBufferDirty(buf);
 
 	/* XLOG stuff */
-	if (logit && !seqrel->rd_istemp)
+	if (logit && RelationNeedsWAL(seqrel))
 	{
 		xl_seq_rec	xlrec;
 		XLogRecPtr	recptr;
@@ -855,7 +855,7 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	MarkBufferDirty(buf);
 
 	/* XLOG stuff */
-	if (!seqrel->rd_istemp)
+	if (RelationNeedsWAL(seqrel))
 	{
 		xl_seq_rec	xlrec;
 		XLogRecPtr	recptr;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 937992ba585e41751bcaed784dcce4ee75819910..6729d8336f570e59fd514fef6bab75dd3bb16ff3 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -224,7 +224,7 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
 
 
 static void truncate_check_rel(Relation rel);
-static List *MergeAttributes(List *schema, List *supers, bool istemp,
+static List *MergeAttributes(List *schema, List *supers, char relpersistence,
 				List **supOids, List **supconstr, int *supOidCount);
 static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
 static bool change_varattnos_walker(Node *node, const AttrNumber *newattno);
@@ -339,7 +339,7 @@ static void ATPrepAddInherit(Relation child_rel);
 static void ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
 static void ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
 static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
-				   ForkNumber forkNum, bool istemp);
+				   ForkNumber forkNum, char relpersistence);
 static const char *storage_name(char c);
 
 
@@ -391,7 +391,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	/*
 	 * Check consistency of arguments
 	 */
-	if (stmt->oncommit != ONCOMMIT_NOOP && !stmt->relation->istemp)
+	if (stmt->oncommit != ONCOMMIT_NOOP 
+		&& stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 				 errmsg("ON COMMIT can only be used on temporary tables")));
@@ -401,7 +402,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	 * code.  This is needed because calling code might not expect untrusted
 	 * tables to appear in pg_temp at the front of its search path.
 	 */
-	if (stmt->relation->istemp && InSecurityRestrictedOperation())
+	if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
+		&& InSecurityRestrictedOperation())
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("cannot create temporary table within security-restricted operation")));
@@ -434,7 +436,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	}
 	else
 	{
-		tablespaceId = GetDefaultTablespace(stmt->relation->istemp);
+		tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence);
 		/* note InvalidOid is OK in this case */
 	}
 
@@ -478,7 +480,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	 * inherited attributes.
 	 */
 	schema = MergeAttributes(schema, stmt->inhRelations,
-							 stmt->relation->istemp,
+							 stmt->relation->relpersistence,
 							 &inheritOids, &old_constraints, &parentOidCount);
 
 	/*
@@ -557,6 +559,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 										  list_concat(cookedDefaults,
 													  old_constraints),
 										  relkind,
+										  stmt->relation->relpersistence,
 										  false,
 										  false,
 										  localHasOids,
@@ -1208,7 +1211,7 @@ storage_name(char c)
  *----------
  */
 static List *
-MergeAttributes(List *schema, List *supers, bool istemp,
+MergeAttributes(List *schema, List *supers, char relpersistence,
 				List **supOids, List **supconstr, int *supOidCount)
 {
 	ListCell   *entry;
@@ -1316,7 +1319,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 					 errmsg("inherited relation \"%s\" is not a table",
 							parent->relname)));
 		/* Permanent rels cannot inherit from temporary ones */
-		if (!istemp && relation->rd_istemp)
+		if (relpersistence != RELPERSISTENCE_TEMP
+			&& RelationUsesTempNamespace(relation))
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("cannot inherit from temporary relation \"%s\"",
@@ -5124,26 +5128,27 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
 						RelationGetRelationName(pkrel))));
 
 	/*
-	 * Disallow reference from permanent table to temp table or vice versa.
-	 * (The ban on perm->temp is for fairly obvious reasons.  The ban on
-	 * temp->perm is because other backends might need to run the RI triggers
-	 * on the perm table, but they can't reliably see tuples the owning
-	 * backend has created in the temp table, because non-shared buffers are
-	 * used for temp tables.)
+	 * References from permanent tables to temp tables are disallowed because
+	 * the contents of the temp table disappear at the end of each session.
+	 * References from temp tables to permanent tables are also disallowed,
+	 * because other backends might need to run the RI triggers on the perm
+	 * table, but they can't reliably see tuples in the local buffers of other
+	 * backends.
 	 */
-	if (pkrel->rd_istemp)
+	switch (rel->rd_rel->relpersistence)
 	{
-		if (!rel->rd_istemp)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-					 errmsg("cannot reference temporary table from permanent table constraint")));
-	}
-	else
-	{
-		if (rel->rd_istemp)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-					 errmsg("cannot reference permanent table from temporary table constraint")));
+		case RELPERSISTENCE_PERMANENT:
+			if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("constraints on permanent tables may reference only permanent tables")));
+			break;
+		case RELPERSISTENCE_TEMP:
+			if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("constraints on temporary tables may reference only temporary tables")));
+			break;
 	}
 
 	/*
@@ -7347,7 +7352,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 	 * Relfilenodes are not unique across tablespaces, so we need to allocate
 	 * a new one in the new tablespace.
 	 */
-	newrelfilenode = GetNewRelFileNode(newTableSpace, NULL, rel->rd_backend);
+	newrelfilenode = GetNewRelFileNode(newTableSpace, NULL,
+									   rel->rd_rel->relpersistence);
 
 	/* Open old and new relation */
 	newrnode = rel->rd_node;
@@ -7364,10 +7370,11 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 	 * NOTE: any conflict in relfilenode value will be caught in
 	 * RelationCreateStorage().
 	 */
-	RelationCreateStorage(newrnode, rel->rd_istemp);
+	RelationCreateStorage(newrnode, rel->rd_rel->relpersistence);
 
 	/* copy main fork */
-	copy_relation_data(rel->rd_smgr, dstrel, MAIN_FORKNUM, rel->rd_istemp);
+	copy_relation_data(rel->rd_smgr, dstrel, MAIN_FORKNUM,
+					   rel->rd_rel->relpersistence);
 
 	/* copy those extra forks that exist */
 	for (forkNum = MAIN_FORKNUM + 1; forkNum <= MAX_FORKNUM; forkNum++)
@@ -7375,7 +7382,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 		if (smgrexists(rel->rd_smgr, forkNum))
 		{
 			smgrcreate(dstrel, forkNum, false);
-			copy_relation_data(rel->rd_smgr, dstrel, forkNum, rel->rd_istemp);
+			copy_relation_data(rel->rd_smgr, dstrel, forkNum,
+							   rel->rd_rel->relpersistence);
 		}
 	}
 
@@ -7410,7 +7418,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
  */
 static void
 copy_relation_data(SMgrRelation src, SMgrRelation dst,
-				   ForkNumber forkNum, bool istemp)
+				   ForkNumber forkNum, char relpersistence)
 {
 	char	   *buf;
 	Page		page;
@@ -7429,9 +7437,9 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
 
 	/*
 	 * We need to log the copied data in WAL iff WAL archiving/streaming is
-	 * enabled AND it's not a temp rel.
+	 * enabled AND it's a permanent relation.
 	 */
-	use_wal = XLogIsNeeded() && !istemp;
+	use_wal = XLogIsNeeded() && relpersistence == RELPERSISTENCE_PERMANENT;
 
 	nblocks = smgrnblocks(src, forkNum);
 
@@ -7470,7 +7478,7 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
 	 * wouldn't replay our earlier WAL entries. If we do not fsync those pages
 	 * here, they might still not be on disk when the crash occurs.
 	 */
-	if (!istemp)
+	if (relpersistence == RELPERSISTENCE_PERMANENT)
 		smgrimmedsync(dst, forkNum);
 }
 
@@ -7538,7 +7546,8 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 	ATSimplePermissions(parent_rel, false, false);
 
 	/* Permanent rels cannot inherit from temporary ones */
-	if (parent_rel->rd_istemp && !child_rel->rd_istemp)
+	if (RelationUsesTempNamespace(parent_rel)
+		&& !RelationUsesTempNamespace(child_rel))
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("cannot inherit from temporary relation \"%s\"",
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index cd80c811a92963bcb9680538c41cd18b505a7d0d..5ad08314049246336b7511085ff5ce8d6710890b 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -1050,8 +1050,8 @@ assign_default_tablespace(const char *newval, bool doit, GucSource source)
 /*
  * GetDefaultTablespace -- get the OID of the current default tablespace
  *
- * Regular objects and temporary objects have different default tablespaces,
- * hence the forTemp parameter must be specified.
+ * Temporary objects have different default tablespaces, hence the
+ * relpersistence parameter must be specified.
  *
  * May return InvalidOid to indicate "use the database's default tablespace".
  *
@@ -1062,12 +1062,12 @@ assign_default_tablespace(const char *newval, bool doit, GucSource source)
  * default_tablespace GUC variable.
  */
 Oid
-GetDefaultTablespace(bool forTemp)
+GetDefaultTablespace(char relpersistence)
 {
 	Oid			result;
 
 	/* The temp-table case is handled elsewhere */
-	if (forTemp)
+	if (relpersistence == RELPERSISTENCE_TEMP)
 	{
 		PrepareTempTablespaces();
 		return GetNextTempTableSpace();
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 0ac993f957d6e49c23448dd85f8ed41308dc9c60..cbdf97de91bee3f084db3df124cc00899c7ed5cf 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -268,10 +268,10 @@ static void
 vacuum_log_cleanup_info(Relation rel, LVRelStats *vacrelstats)
 {
 	/*
-	 * No need to log changes for temp tables, they do not contain data
-	 * visible on the standby server.
+	 * Skip this for relations for which no WAL is to be written, or if we're
+	 * not trying to support archive recovery.
 	 */
-	if (rel->rd_istemp || !XLogIsNeeded())
+	if (!RelationNeedsWAL(rel) || !XLogIsNeeded())
 		return;
 
 	/*
@@ -664,8 +664,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 		if (nfrozen > 0)
 		{
 			MarkBufferDirty(buf);
-			/* no XLOG for temp tables, though */
-			if (!onerel->rd_istemp)
+			if (RelationNeedsWAL(onerel))
 			{
 				XLogRecPtr	recptr;
 
@@ -895,7 +894,7 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
 	MarkBufferDirty(buffer);
 
 	/* XLOG stuff */
-	if (!onerel->rd_istemp)
+	if (RelationNeedsWAL(onerel))
 	{
 		XLogRecPtr	recptr;
 
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 09ab24b011601f6d16cc200b03b6e8973fcc5f9b..2b2b90850050e9bce946d2b29f55bb8693daab55 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -68,10 +68,10 @@ isViewOnTempTable_walker(Node *node, void *context)
 			if (rte->rtekind == RTE_RELATION)
 			{
 				Relation	rel = heap_open(rte->relid, AccessShareLock);
-				bool		istemp = rel->rd_istemp;
+				char		relpersistence = rel->rd_rel->relpersistence;
 
 				heap_close(rel, AccessShareLock);
-				if (istemp)
+				if (relpersistence == RELPERSISTENCE_TEMP)
 					return true;
 			}
 		}
@@ -173,9 +173,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
 		/*
 		 * Due to the namespace visibility rules for temporary objects, we
 		 * should only end up replacing a temporary view with another
-		 * temporary view, and vice versa.
+		 * temporary view, and similarly for permanent views.
 		 */
-		Assert(relation->istemp == rel->rd_istemp);
+		Assert(relation->relpersistence == rel->rd_rel->relpersistence);
 
 		/*
 		 * Create a tuple descriptor to compare against the existing view, and
@@ -454,10 +454,11 @@ DefineView(ViewStmt *stmt, const char *queryString)
 	 * schema name.
 	 */
 	view = stmt->view;
-	if (!view->istemp && isViewOnTempTable(viewParse))
+	if (view->relpersistence == RELPERSISTENCE_PERMANENT
+		&& isViewOnTempTable(viewParse))
 	{
 		view = copyObject(view);	/* don't corrupt original command */
-		view->istemp = true;
+		view->relpersistence = RELPERSISTENCE_TEMP;
 		ereport(NOTICE,
 				(errmsg("view \"%s\" will be a temporary view",
 						view->relname)));
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 69f3a28d4151558466ddbc58256c7775eedc2f77..c4719f33b96aa92df194127d026aef21b4ebb56d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2131,7 +2131,8 @@ OpenIntoRel(QueryDesc *queryDesc)
 	/*
 	 * Check consistency of arguments
 	 */
-	if (into->onCommit != ONCOMMIT_NOOP && !into->rel->istemp)
+	if (into->onCommit != ONCOMMIT_NOOP
+		&& into->rel->relpersistence != RELPERSISTENCE_TEMP)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 				 errmsg("ON COMMIT can only be used on temporary tables")));
@@ -2141,7 +2142,8 @@ OpenIntoRel(QueryDesc *queryDesc)
 	 * code.  This is needed because calling code might not expect untrusted
 	 * tables to appear in pg_temp at the front of its search path.
 	 */
-	if (into->rel->istemp && InSecurityRestrictedOperation())
+	if (into->rel->relpersistence == RELPERSISTENCE_TEMP
+		&& InSecurityRestrictedOperation())
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("cannot create temporary table within security-restricted operation")));
@@ -2168,7 +2170,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 	}
 	else
 	{
-		tablespaceId = GetDefaultTablespace(into->rel->istemp);
+		tablespaceId = GetDefaultTablespace(into->rel->relpersistence);
 		/* note InvalidOid is OK in this case */
 	}
 
@@ -2208,6 +2210,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 											  tupdesc,
 											  NIL,
 											  RELKIND_RELATION,
+											  into->rel->relpersistence,
 											  false,
 											  false,
 											  true,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 735322ee3f5842cf27dc14f4e46be04ac3743e11..4e1f221af1b2ed70a7c3165c18049a29d03717d9 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -958,7 +958,7 @@ _copyRangeVar(RangeVar *from)
 	COPY_STRING_FIELD(schemaname);
 	COPY_STRING_FIELD(relname);
 	COPY_SCALAR_FIELD(inhOpt);
-	COPY_SCALAR_FIELD(istemp);
+	COPY_SCALAR_FIELD(relpersistence);
 	COPY_NODE_FIELD(alias);
 	COPY_LOCATION_FIELD(location);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2d2b8c77634e1ceb5b41a96404df9a097ce2a584..85cded0f74dd329297491be46282d5b8ec80ae0e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -104,7 +104,7 @@ _equalRangeVar(RangeVar *a, RangeVar *b)
 	COMPARE_STRING_FIELD(schemaname);
 	COMPARE_STRING_FIELD(relname);
 	COMPARE_SCALAR_FIELD(inhOpt);
-	COMPARE_SCALAR_FIELD(istemp);
+	COMPARE_SCALAR_FIELD(relpersistence);
 	COMPARE_NODE_FIELD(alias);
 	COMPARE_LOCATION_FIELD(location);
 
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 4b268f3c6b3bc2ac5a4566d71c77477af8e4046b..f06f73bd6ac2baa6e4eaf0fe77e6b0c8f5108d8d 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -15,6 +15,7 @@
  */
 #include "postgres.h"
 
+#include "catalog/pg_class.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -378,7 +379,7 @@ makeRangeVar(char *schemaname, char *relname, int location)
 	r->schemaname = schemaname;
 	r->relname = relname;
 	r->inhOpt = INH_DEFAULT;
-	r->istemp = false;
+	r->relpersistence = RELPERSISTENCE_PERMANENT;
 	r->alias = NULL;
 	r->location = location;
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 5d09e16477d18f3e56328bc3d020846921d98cf9..7d77d84a37008fab1be76b4ee86bc6df28cca4d8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -841,7 +841,7 @@ _outRangeVar(StringInfo str, RangeVar *node)
 	WRITE_STRING_FIELD(schemaname);
 	WRITE_STRING_FIELD(relname);
 	WRITE_ENUM_FIELD(inhOpt, InhOption);
-	WRITE_BOOL_FIELD(istemp);
+	WRITE_CHAR_FIELD(relpersistence);
 	WRITE_NODE_FIELD(alias);
 	WRITE_LOCATION_FIELD(location);
 }
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 2166a5d1e0e9f359f0420e2fa9384af354dc0027..933d58ada5f59fddbbc0224c9a6f5c6748726b8c 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -373,7 +373,7 @@ _readRangeVar(void)
 	READ_STRING_FIELD(schemaname);
 	READ_STRING_FIELD(relname);
 	READ_ENUM_FIELD(inhOpt, InhOption);
-	READ_BOOL_FIELD(istemp);
+	READ_CHAR_FIELD(relpersistence);
 	READ_NODE_FIELD(alias);
 	READ_LOCATION_FIELD(location);
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9ec75f776cff61bd1ae497234c31154acb52c3dc..8fc79b63377c93159f4a069e61dd457d39d1dbc8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -311,7 +311,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <fun_param_mode> arg_class
 %type <typnam>	func_return func_type
 
-%type <boolean>  OptTemp opt_trusted opt_restart_seqs
+%type <boolean>  opt_trusted opt_restart_seqs
+%type <ival>	 OptTemp
 %type <oncommit> OnCommitOption
 
 %type <node>	for_locking_item
@@ -2280,7 +2281,7 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 			OptInherit OptWith OnCommitOption OptTableSpace
 				{
 					CreateStmt *n = makeNode(CreateStmt);
-					$4->istemp = $2;
+					$4->relpersistence = $2;
 					n->relation = $4;
 					n->tableElts = $6;
 					n->inhRelations = $8;
@@ -2296,7 +2297,7 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 			OptTableSpace
 				{
 					CreateStmt *n = makeNode(CreateStmt);
-					$7->istemp = $2;
+					$7->relpersistence = $2;
 					n->relation = $7;
 					n->tableElts = $9;
 					n->inhRelations = $11;
@@ -2311,7 +2312,7 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 			OptTypedTableElementList OptWith OnCommitOption OptTableSpace
 				{
 					CreateStmt *n = makeNode(CreateStmt);
-					$4->istemp = $2;
+					$4->relpersistence = $2;
 					n->relation = $4;
 					n->tableElts = $7;
 					n->ofTypename = makeTypeNameFromNameList($6);
@@ -2327,7 +2328,7 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 			OptTypedTableElementList OptWith OnCommitOption OptTableSpace
 				{
 					CreateStmt *n = makeNode(CreateStmt);
-					$7->istemp = $2;
+					$7->relpersistence = $2;
 					n->relation = $7;
 					n->tableElts = $10;
 					n->ofTypename = makeTypeNameFromNameList($9);
@@ -2348,13 +2349,13 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
  * NOTE: we accept both GLOBAL and LOCAL options; since we have no modules
  * the LOCAL keyword is really meaningless.
  */
-OptTemp:	TEMPORARY						{ $$ = TRUE; }
-			| TEMP							{ $$ = TRUE; }
-			| LOCAL TEMPORARY				{ $$ = TRUE; }
-			| LOCAL TEMP					{ $$ = TRUE; }
-			| GLOBAL TEMPORARY				{ $$ = TRUE; }
-			| GLOBAL TEMP					{ $$ = TRUE; }
-			| /*EMPTY*/						{ $$ = FALSE; }
+OptTemp:	TEMPORARY					{ $$ = RELPERSISTENCE_TEMP; }
+			| TEMP						{ $$ = RELPERSISTENCE_TEMP; }
+			| LOCAL TEMPORARY			{ $$ = RELPERSISTENCE_TEMP; }
+			| LOCAL TEMP				{ $$ = RELPERSISTENCE_TEMP; }
+			| GLOBAL TEMPORARY			{ $$ = RELPERSISTENCE_TEMP; }
+			| GLOBAL TEMP				{ $$ = RELPERSISTENCE_TEMP; }
+			| /*EMPTY*/					{ $$ = RELPERSISTENCE_PERMANENT; }
 		;
 
 OptTableElementList:
@@ -2834,7 +2835,7 @@ CreateAsStmt:
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("CREATE TABLE AS cannot specify INTO"),
 								 parser_errposition(exprLocation((Node *) n->intoClause))));
-					$4->rel->istemp = $2;
+					$4->rel->relpersistence = $2;
 					n->intoClause = $4;
 					/* Implement WITH NO DATA by forcing top-level LIMIT 0 */
 					if (!$7)
@@ -2900,7 +2901,7 @@ CreateSeqStmt:
 			CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
 				{
 					CreateSeqStmt *n = makeNode(CreateSeqStmt);
-					$4->istemp = $2;
+					$4->relpersistence = $2;
 					n->sequence = $4;
 					n->options = $5;
 					n->ownerId = InvalidOid;
@@ -6621,7 +6622,7 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list
 				{
 					ViewStmt *n = makeNode(ViewStmt);
 					n->view = $4;
-					n->view->istemp = $2;
+					n->view->relpersistence = $2;
 					n->aliases = $5;
 					n->query = $7;
 					n->replace = false;
@@ -6632,7 +6633,7 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list
 				{
 					ViewStmt *n = makeNode(ViewStmt);
 					n->view = $6;
-					n->view->istemp = $4;
+					n->view->relpersistence = $4;
 					n->aliases = $7;
 					n->query = $9;
 					n->replace = true;
@@ -7328,7 +7329,7 @@ ExecuteStmt: EXECUTE name execute_param_clause
 					ExecuteStmt *n = makeNode(ExecuteStmt);
 					n->name = $7;
 					n->params = $8;
-					$4->rel->istemp = $2;
+					$4->rel->relpersistence = $2;
 					n->into = $4;
 					if ($4->colNames)
 						ereport(ERROR,
@@ -7889,42 +7890,42 @@ OptTempTableName:
 			TEMPORARY opt_table qualified_name
 				{
 					$$ = $3;
-					$$->istemp = true;
+					$$->relpersistence = RELPERSISTENCE_TEMP;
 				}
 			| TEMP opt_table qualified_name
 				{
 					$$ = $3;
-					$$->istemp = true;
+					$$->relpersistence = RELPERSISTENCE_TEMP;
 				}
 			| LOCAL TEMPORARY opt_table qualified_name
 				{
 					$$ = $4;
-					$$->istemp = true;
+					$$->relpersistence = RELPERSISTENCE_TEMP;
 				}
 			| LOCAL TEMP opt_table qualified_name
 				{
 					$$ = $4;
-					$$->istemp = true;
+					$$->relpersistence = RELPERSISTENCE_TEMP;
 				}
 			| GLOBAL TEMPORARY opt_table qualified_name
 				{
 					$$ = $4;
-					$$->istemp = true;
+					$$->relpersistence = RELPERSISTENCE_TEMP;
 				}
 			| GLOBAL TEMP opt_table qualified_name
 				{
 					$$ = $4;
-					$$->istemp = true;
+					$$->relpersistence = RELPERSISTENCE_TEMP;
 				}
 			| TABLE qualified_name
 				{
 					$$ = $2;
-					$$->istemp = false;
+					$$->relpersistence = RELPERSISTENCE_PERMANENT;
 				}
 			| qualified_name
 				{
 					$$ = $1;
-					$$->istemp = false;
+					$$->relpersistence = RELPERSISTENCE_PERMANENT;
 				}
 		;
 
@@ -10916,16 +10917,12 @@ qualified_name_list:
 qualified_name:
 			ColId
 				{
-					$$ = makeNode(RangeVar);
-					$$->catalogname = NULL;
-					$$->schemaname = NULL;
-					$$->relname = $1;
-					$$->location = @1;
+					$$ = makeRangeVar(NULL, $1, @1);
 				}
 			| ColId indirection
 				{
 					check_qualified_name($2, yyscanner);
-					$$ = makeNode(RangeVar);
+					$$ = makeRangeVar(NULL, NULL, @1);
 					switch (list_length($2))
 					{
 						case 1:
@@ -10946,7 +10943,6 @@ qualified_name:
 									 parser_errposition(@1)));
 							break;
 					}
-					$$->location = @1;
 				}
 		;
 
@@ -12163,6 +12159,7 @@ makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner)
 			break;
 	}
 
+	r->relpersistence = RELPERSISTENCE_PERMANENT;
 	r->location = position;
 
 	return r;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index a8aee204c74a5c66fb7bf43271bbc8d226787fc2..aa7c144c941ac99295adacc0755d133ecc98bc24 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -158,10 +158,11 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
 	 * If the target relation name isn't schema-qualified, make it so.  This
 	 * prevents some corner cases in which added-on rewritten commands might
 	 * think they should apply to other relations that have the same name and
-	 * are earlier in the search path.	"istemp" is equivalent to a
-	 * specification of pg_temp, so no need for anything extra in that case.
+	 * are earlier in the search path.	But a local temp table is effectively
+	 * specified to be in pg_temp, so no need for anything extra in that case.
 	 */
-	if (stmt->relation->schemaname == NULL && !stmt->relation->istemp)
+	if (stmt->relation->schemaname == NULL
+		&& stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
 	{
 		Oid			namespaceid = RangeVarGetCreationNamespace(stmt->relation);
 
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index c7d704dc8c80253698a882cbccef4227a92963a7..89b2540871c42d31576bc9db6ad553ce42fb2abc 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1975,7 +1975,7 @@ do_autovacuum(void)
 		 * Check if it is a temp table (presumably, of some other backend's).
 		 * We cannot safely process other backends' temp tables.
 		 */
-		if (classForm->relistemp)
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
 		{
 			int			backendID;
 
@@ -2072,7 +2072,7 @@ do_autovacuum(void)
 		/*
 		 * We cannot safely process other backends' temp tables, so skip 'em.
 		 */
-		if (classForm->relistemp)
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
 			continue;
 
 		relid = HeapTupleGetOid(tuple);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index edc497788dfaa0b3d354b20682114406c64d34f3..860e736ff05b5b8a05844cc83df8e48fb16950e0 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -123,7 +123,7 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 	/* Open it at the smgr level if not already done */
 	RelationOpenSmgr(reln);
 
-	if (reln->rd_istemp)
+	if (RelationUsesLocalBuffers(reln))
 	{
 		/* see comments in ReadBufferExtended */
 		if (RELATION_IS_OTHER_TEMP(reln))
@@ -2071,7 +2071,7 @@ FlushRelationBuffers(Relation rel)
 	/* Open rel at the smgr level if not already done */
 	RelationOpenSmgr(rel);
 
-	if (rel->rd_istemp)
+	if (RelationUsesLocalBuffers(rel))
 	{
 		for (i = 0; i < NLocBuffer; i++)
 		{
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index f5250a263cd07f26a20ce7711387c476bfced58b..e352cdafb3bbafbc4f47e95ae66dbb8997e4eb3c 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -612,16 +612,26 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 	}
 
-	/* If temporary, determine owning backend. */
-	if (!relform->relistemp)
-		backend = InvalidBackendId;
-	else if (isTempOrToastNamespace(relform->relnamespace))
-		backend = MyBackendId;
-	else
+	/* Determine owning backend. */
+	switch (relform->relpersistence)
 	{
-		/* Do it the hard way. */
-		backend = GetTempNamespaceBackendId(relform->relnamespace);
-		Assert(backend != InvalidBackendId);
+		case RELPERSISTENCE_PERMANENT:
+			backend = InvalidBackendId;
+			break;
+		case RELPERSISTENCE_TEMP:
+			if (isTempOrToastNamespace(relform->relnamespace))
+				backend = MyBackendId;
+			else
+			{
+				/* Do it the hard way. */
+				backend = GetTempNamespaceBackendId(relform->relnamespace);
+				Assert(backend != InvalidBackendId);
+			}
+			break;
+		default:
+			elog(ERROR, "invalid relpersistence: %c", relform->relpersistence);
+			backend = InvalidBackendId; 	/* placate compiler */
+			break;
 	}
 
 	ReleaseSysCache(tuple);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 8df12a1424322501c2a55a59723a4b21880dfb35..1509686079b0f88f85f72a7a14127be07d711907 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -849,20 +849,30 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	relation->rd_isnailed = false;
 	relation->rd_createSubid = InvalidSubTransactionId;
 	relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
-	relation->rd_istemp = relation->rd_rel->relistemp;
-	if (!relation->rd_istemp)
-		relation->rd_backend = InvalidBackendId;
-	else if (isTempOrToastNamespace(relation->rd_rel->relnamespace))
-		relation->rd_backend = MyBackendId;
-	else
+	switch (relation->rd_rel->relpersistence)
 	{
-		/*
-		 * If it's a temporary table, but not one of ours, we have to use
-		 * the slow, grotty method to figure out the owning backend.
-		 */
-		relation->rd_backend =
-			GetTempNamespaceBackendId(relation->rd_rel->relnamespace);
-		Assert(relation->rd_backend != InvalidBackendId);
+		case RELPERSISTENCE_PERMANENT:
+			relation->rd_backend = InvalidBackendId;
+			break;
+		case RELPERSISTENCE_TEMP:
+			if (isTempOrToastNamespace(relation->rd_rel->relnamespace))
+				relation->rd_backend = MyBackendId;
+			else
+			{
+				/*
+				 * If it's a local temp table, but not one of ours, we have to
+				 * use the slow, grotty method to figure out the owning
+				 * backend.
+				 */
+				relation->rd_backend =
+					GetTempNamespaceBackendId(relation->rd_rel->relnamespace);
+				Assert(relation->rd_backend != InvalidBackendId);
+			}
+			break;
+		default:
+			elog(ERROR, "invalid relpersistence: %c",
+				 relation->rd_rel->relpersistence);
+			break;
 	}
 
 	/*
@@ -1358,7 +1368,6 @@ formrdesc(const char *relationName, Oid relationReltype,
 	relation->rd_isnailed = true;
 	relation->rd_createSubid = InvalidSubTransactionId;
 	relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
-	relation->rd_istemp = false;
 	relation->rd_backend = InvalidBackendId;
 
 	/*
@@ -1384,11 +1393,8 @@ formrdesc(const char *relationName, Oid relationReltype,
 	if (isshared)
 		relation->rd_rel->reltablespace = GLOBALTABLESPACE_OID;
 
-	/*
-	 * Likewise, we must know if a relation is temp ... but formrdesc is not
-	 * used for any temp relations.
-	 */
-	relation->rd_rel->relistemp = false;
+	/* formrdesc is used only for permanent relations */
+	relation->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
 
 	relation->rd_rel->relpages = 1;
 	relation->rd_rel->reltuples = 1;
@@ -2366,7 +2372,8 @@ RelationBuildLocalRelation(const char *relname,
 						   Oid relid,
 						   Oid reltablespace,
 						   bool shared_relation,
-						   bool mapped_relation)
+						   bool mapped_relation,
+						   char relpersistence)
 {
 	Relation	rel;
 	MemoryContext oldcxt;
@@ -2440,10 +2447,6 @@ RelationBuildLocalRelation(const char *relname,
 	/* must flag that we have rels created in this transaction */
 	need_eoxact_work = true;
 
-	/* it is temporary if and only if it is in my temp-table namespace */
-	rel->rd_istemp = isTempOrToastNamespace(relnamespace);
-	rel->rd_backend = rel->rd_istemp ? MyBackendId : InvalidBackendId;
-
 	/*
 	 * create a new tuple descriptor from the one passed in.  We do this
 	 * partly to copy it into the cache context, and partly because the new
@@ -2483,6 +2486,21 @@ RelationBuildLocalRelation(const char *relname,
 	/* needed when bootstrapping: */
 	rel->rd_rel->relowner = BOOTSTRAP_SUPERUSERID;
 
+	/* set up persistence; rd_backend is a function of persistence type */
+	rel->rd_rel->relpersistence = relpersistence;
+	switch (relpersistence)
+	{
+		case RELPERSISTENCE_PERMANENT:
+			rel->rd_backend = InvalidBackendId;
+			break;
+		case RELPERSISTENCE_TEMP:
+			rel->rd_backend = MyBackendId;
+			break;
+		default:
+			elog(ERROR, "invalid relpersistence: %c", relpersistence);
+			break;
+	}
+
 	/*
 	 * Insert relation physical and logical identifiers (OIDs) into the right
 	 * places.	Note that the physical ID (relfilenode) is initially the same
@@ -2491,7 +2509,6 @@ RelationBuildLocalRelation(const char *relname,
 	 * map.
 	 */
 	rel->rd_rel->relisshared = shared_relation;
-	rel->rd_rel->relistemp = rel->rd_istemp;
 
 	RelationGetRelid(rel) = relid;
 
@@ -2569,7 +2586,7 @@ RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid)
 
 	/* Allocate a new relfilenode */
 	newrelfilenode = GetNewRelFileNode(relation->rd_rel->reltablespace, NULL,
-									   relation->rd_backend);
+									   relation->rd_rel->relpersistence);
 
 	/*
 	 * Get a writable copy of the pg_class tuple for the given relation.
@@ -2592,7 +2609,7 @@ RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid)
 	newrnode.node = relation->rd_node;
 	newrnode.node.relNode = newrelfilenode;
 	newrnode.backend = relation->rd_backend;
-	RelationCreateStorage(newrnode.node, relation->rd_istemp);
+	RelationCreateStorage(newrnode.node, relation->rd_rel->relpersistence);
 	smgrclosenode(newrnode);
 
 	/*
diff --git a/src/include/catalog/catalog.h b/src/include/catalog/catalog.h
index 97c808bc509fb08cf627dee14c391ddefc6a86b1..56dcdd53fe2596af646c8f115caa73cdfebbefc3 100644
--- a/src/include/catalog/catalog.h
+++ b/src/include/catalog/catalog.h
@@ -56,6 +56,6 @@ extern Oid	GetNewOid(Relation relation);
 extern Oid GetNewOidWithIndex(Relation relation, Oid indexId,
 				   AttrNumber oidcolumn);
 extern Oid	GetNewRelFileNode(Oid reltablespace, Relation pg_class,
-				  BackendId backend);
+				  char relpersistence);
 
 #endif   /* CATALOG_H */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 6c12f7cbf8278eb06c1d676a48fb671d916e5e41..1c3d14951c0d38a109cec893c779b76fbae2849c 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201012031
+#define CATALOG_VERSION_NO	201012131
 
 #endif
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 7795bda323b89642e67e11618e0e03b040f4239f..646ab9c8f191601115da464cb289ef97d736d422 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -40,6 +40,7 @@ extern Relation heap_create(const char *relname,
 			Oid relid,
 			TupleDesc tupDesc,
 			char relkind,
+			char relpersistence,
 			bool shared_relation,
 			bool mapped_relation,
 			bool allow_system_table_mods);
@@ -54,6 +55,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 TupleDesc tupdesc,
 						 List *cooked_constraints,
 						 char relkind,
+						 char relpersistence,
 						 bool shared_relation,
 						 bool mapped_relation,
 						 bool oidislocal,
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index f50cf9d55bbe9601a6e3c69d20b4b370b6e43cf5..1edbfe378b115f133dbf5b1987c95965e2e747c9 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -49,7 +49,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
 	Oid			reltoastidxid;	/* if toast table, OID of chunk_id index */
 	bool		relhasindex;	/* T if has (or has had) any indexes */
 	bool		relisshared;	/* T if shared across databases */
-	bool		relistemp;		/* T if temporary relation */
+	char		relpersistence;	/* see RELPERSISTENCE_xxx constants */
 	char		relkind;		/* see RELKIND_xxx constants below */
 	int2		relnatts;		/* number of user attributes */
 
@@ -108,7 +108,7 @@ typedef FormData_pg_class *Form_pg_class;
 #define Anum_pg_class_reltoastidxid		12
 #define Anum_pg_class_relhasindex		13
 #define Anum_pg_class_relisshared		14
-#define Anum_pg_class_relistemp			15
+#define Anum_pg_class_relpersistence	15
 #define Anum_pg_class_relkind			16
 #define Anum_pg_class_relnatts			17
 #define Anum_pg_class_relchecks			18
@@ -132,13 +132,13 @@ typedef FormData_pg_class *Form_pg_class;
  */
 
 /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
-DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 28 0 t f f f f f 3 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 19 0 f f f f f f 3 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 25 0 t f f f f f 3 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1259 (  pg_class		PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 27 0 t f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1259 (  pg_class		PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 27 0 t f f f f f 3 _null_ _null_ ));
 DESCR("");
 
 #define		  RELKIND_INDEX			  'i'		/* secondary index */
@@ -149,4 +149,7 @@ DESCR("");
 #define		  RELKIND_VIEW			  'v'		/* view */
 #define		  RELKIND_COMPOSITE_TYPE  'c'		/* composite type */
 
+#define		  RELPERSISTENCE_PERMANENT	'p'
+#define		  RELPERSISTENCE_TEMP		't'
+
 #endif   /* PG_CLASS_H */
diff --git a/src/include/catalog/storage.h b/src/include/catalog/storage.h
index d7b8731838cb21e9e85957908f7812d3dc8a1123..f086b1c33f673f359592a13419fa0cc17c4fd177 100644
--- a/src/include/catalog/storage.h
+++ b/src/include/catalog/storage.h
@@ -20,7 +20,7 @@
 #include "storage/relfilenode.h"
 #include "utils/relcache.h"
 
-extern void RelationCreateStorage(RelFileNode rnode, bool istemp);
+extern void RelationCreateStorage(RelFileNode rnode, char relpersistence);
 extern void RelationDropStorage(Relation rel);
 extern void RelationPreserveStorage(RelFileNode rnode);
 extern void RelationTruncate(Relation rel, BlockNumber nblocks);
diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h
index 327fbc6c4f9c28b4f8b3ecaf31c15438150cf878..1e3f6ca0c6225b2b15884c662eea93dee996b0b7 100644
--- a/src/include/commands/tablespace.h
+++ b/src/include/commands/tablespace.h
@@ -47,7 +47,7 @@ extern void AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt);
 
 extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo);
 
-extern Oid	GetDefaultTablespace(bool forTemp);
+extern Oid	GetDefaultTablespace(char relpersistence);
 
 extern void PrepareTempTablespaces(void);
 
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index b17adf2aa3f79ce82c19e35db96a1d64bb4d3e5e..ba5ae371c01ecba7ae4f2d97110ff71b3c592f88 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -74,7 +74,7 @@ typedef struct RangeVar
 	char	   *relname;		/* the relation/sequence name */
 	InhOption	inhOpt;			/* expand rel by inheritance? recursively act
 								 * on children? */
-	bool		istemp;			/* is this a temp relation/sequence? */
+	char		relpersistence;	/* see RELPERSISTENCE_* in pg_class.h */
 	Alias	   *alias;			/* table alias & optional column aliases */
 	int			location;		/* token location, or -1 if unknown */
 } RangeVar;
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 39e0365c0b124022f2ec487d8be6a4fa2668760d..88a3168d133b31f4e468fb3e13a5f0e4d71c7749 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -132,7 +132,6 @@ typedef struct RelationData
 	struct SMgrRelationData *rd_smgr;	/* cached file handle, or NULL */
 	int			rd_refcnt;		/* reference count */
 	BackendId	rd_backend;		/* owning backend id, if temporary relation */
-	bool		rd_istemp;		/* rel is a temporary relation */
 	bool		rd_isnailed;	/* rel is nailed in cache */
 	bool		rd_isvalid;		/* relcache entry is valid */
 	char		rd_indexvalid;	/* state of rd_indexlist: 0 = not valid, 1 =
@@ -389,6 +388,27 @@ typedef struct StdRdOptions
 		(relation)->rd_smgr->smgr_targblock = (targblock); \
 	} while (0)
 
+/*
+ * RelationNeedsWAL
+ *		True if relation needs WAL.
+ */
+#define RelationNeedsWAL(relation) \
+	((relation)->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
+
+/*
+ * RelationUsesLocalBuffers
+ *		True if relation's pages are stored in local buffers.
+ */
+#define RelationUsesLocalBuffers(relation) \
+	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+
+/*
+ * RelationUsesTempNamespace
+ *		True if relation's catalog entries live in a private namespace.
+ */
+#define RelationUsesTempNamespace(relation) \
+	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+
 /*
  * RELATION_IS_LOCAL
  *		If a rel is either temp or newly created in the current transaction,
@@ -407,7 +427,8 @@ typedef struct StdRdOptions
  * Beware of multiple eval of argument
  */
 #define RELATION_IS_OTHER_TEMP(relation) \
-	((relation)->rd_istemp && (relation)->rd_backend != MyBackendId)
+	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP \
+	&& (relation)->rd_backend != MyBackendId)
 
 /* routines in utils/cache/relcache.c */
 extern void RelationIncrementReferenceCount(Relation rel);
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 10d82d4b412ff2822028eeef9449d4277ce12926..35000500c11e89e40b7054c9a0d4fc43003ccec2 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -69,7 +69,8 @@ extern Relation RelationBuildLocalRelation(const char *relname,
 						   Oid relid,
 						   Oid reltablespace,
 						   bool shared_relation,
-						   bool mapped_relation);
+						   bool mapped_relation,
+						   char relpersistence);
 
 /*
  * Routine to manage assignment of new relfilenode to a relation