I have added support of all indexes (brin, btree, gin, gist, hash, spgist) for global temp tables (before only B-Tree index was supported). It will be nice to have some generic mechanism for it, but I do not understand how it can look like. The problem is that normal relations are initialized at the moment of their creation. But for global temp relations metadata already exists while data is absent. We should somehow catch such access to not initialized page (but not not all pages, but just first page of relation)
and perform initialization on demand.

New patch for global temp tables with shared buffers is attached.

--
Konstantin Knizhnik
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c
index 1bd579f..2d93f6f 100644
--- a/contrib/pg_buffercache/pg_buffercache_pages.c
+++ b/contrib/pg_buffercache/pg_buffercache_pages.c
@@ -153,9 +153,9 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 			buf_state = LockBufHdr(bufHdr);
 
 			fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
-			fctx->record[i].relfilenode = bufHdr->tag.rnode.relNode;
-			fctx->record[i].reltablespace = bufHdr->tag.rnode.spcNode;
-			fctx->record[i].reldatabase = bufHdr->tag.rnode.dbNode;
+			fctx->record[i].relfilenode = bufHdr->tag.rnode.node.relNode;
+			fctx->record[i].reltablespace = bufHdr->tag.rnode.node.spcNode;
+			fctx->record[i].reldatabase = bufHdr->tag.rnode.node.dbNode;
 			fctx->record[i].forknum = bufHdr->tag.forkNum;
 			fctx->record[i].blocknum = bufHdr->tag.blockNum;
 			fctx->record[i].usagecount = BUF_STATE_GET_USAGECOUNT(buf_state);
diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index 38ae240..8a04954 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -608,9 +608,9 @@ apw_dump_now(bool is_bgworker, bool dump_unlogged)
 		if (buf_state & BM_TAG_VALID &&
 			((buf_state & BM_PERMANENT) || dump_unlogged))
 		{
-			block_info_array[num_blocks].database = bufHdr->tag.rnode.dbNode;
-			block_info_array[num_blocks].tablespace = bufHdr->tag.rnode.spcNode;
-			block_info_array[num_blocks].filenode = bufHdr->tag.rnode.relNode;
+			block_info_array[num_blocks].database = bufHdr->tag.rnode.node.dbNode;
+			block_info_array[num_blocks].tablespace = bufHdr->tag.rnode.node.spcNode;
+			block_info_array[num_blocks].filenode = bufHdr->tag.rnode.node.relNode;
 			block_info_array[num_blocks].forknum = bufHdr->tag.forkNum;
 			block_info_array[num_blocks].blocknum = bufHdr->tag.blockNum;
 			++num_blocks;
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index a2c44a9..43b4c66 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -158,7 +158,8 @@ pgrowlocks(PG_FUNCTION_ARGS)
 		/* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
 		LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
 
-		htsu = HeapTupleSatisfiesUpdate(tuple,
+		htsu = HeapTupleSatisfiesUpdate(mydata->rel,
+										tuple,
 										GetCurrentCommandId(false),
 										hscan->rs_cbuf);
 		xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 70af43e..9cce720 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -349,7 +349,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
 		/* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
 		LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
 
-		if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, hscan->rs_cbuf))
+		if (HeapTupleSatisfiesVisibility(rel, tuple, &SnapshotDirty, hscan->rs_cbuf))
 		{
 			stat.tuple_len += tuple->t_len;
 			stat.tuple_count++;
diff --git a/src/backend/access/brin/brin_revmap.c b/src/backend/access/brin/brin_revmap.c
index e2bfbf8..97041a8 100644
--- a/src/backend/access/brin/brin_revmap.c
+++ b/src/backend/access/brin/brin_revmap.c
@@ -25,6 +25,7 @@
 #include "access/brin_revmap.h"
 #include "access/brin_tuple.h"
 #include "access/brin_xlog.h"
+#include "access/brin.h"
 #include "access/rmgr.h"
 #include "access/xloginsert.h"
 #include "miscadmin.h"
@@ -79,6 +80,11 @@ brinRevmapInitialize(Relation idxrel, BlockNumber *pagesPerRange,
 	meta = ReadBuffer(idxrel, BRIN_METAPAGE_BLKNO);
 	LockBuffer(meta, BUFFER_LOCK_SHARE);
 	page = BufferGetPage(meta);
+
+	if (GlobalTempRelationPageIsNotInitialized(idxrel, page))
+		brin_metapage_init(page, BrinGetPagesPerRange(idxrel),
+						   BRIN_CURRENT_VERSION);
+
 	TestForOldSnapshot(snapshot, idxrel, page);
 	metadata = (BrinMetaPageData *) PageGetContents(page);
 
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index 439a91b..8a6ac71 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -241,6 +241,16 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
 	metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
 	metapage = BufferGetPage(metabuffer);
 
+	if (GlobalTempRelationPageIsNotInitialized(index, metapage))
+	{
+		Buffer rootbuffer = ReadBuffer(index, GIN_ROOT_BLKNO);
+		LockBuffer(rootbuffer, BUFFER_LOCK_EXCLUSIVE);
+		GinInitMetabuffer(metabuffer);
+		GinInitBuffer(rootbuffer, GIN_LEAF);
+		MarkBufferDirty(rootbuffer);
+		UnlockReleaseBuffer(rootbuffer);
+	}
+
 	/*
 	 * An insertion to the pending list could logically belong anywhere in the
 	 * tree, so it conflicts with all serializable scans.  All scans acquire a
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index b18ae2b..41bab5d 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -1750,7 +1750,7 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
 /*
  * Collect all matched rows from pending list into bitmap.
  */
-static void
+static bool
 scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
 {
 	GinScanOpaque so = (GinScanOpaque) scan->opaque;
@@ -1774,6 +1774,12 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
 	LockBuffer(metabuffer, GIN_SHARE);
 	page = BufferGetPage(metabuffer);
 	TestForOldSnapshot(scan->xs_snapshot, scan->indexRelation, page);
+
+	if (GlobalTempRelationPageIsNotInitialized(scan->indexRelation, page))
+	{
+		UnlockReleaseBuffer(metabuffer);
+		return false;
+	}
 	blkno = GinPageGetMeta(page)->head;
 
 	/*
@@ -1784,7 +1790,7 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
 	{
 		/* No pending list, so proceed with normal scan */
 		UnlockReleaseBuffer(metabuffer);
-		return;
+		return true;
 	}
 
 	pos.pendingBuffer = ReadBuffer(scan->indexRelation, blkno);
@@ -1840,6 +1846,7 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
 	}
 
 	pfree(pos.hasMatchKey);
+	return true;
 }
 
 
@@ -1875,7 +1882,8 @@ gingetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 	 * to scan the main index before the pending list, since concurrent
 	 * cleanup could then make us miss entries entirely.
 	 */
-	scanPendingInsert(scan, tbm, &ntids);
+	if (!scanPendingInsert(scan, tbm, &ntids))
+		return 0;
 
 	/*
 	 * Now scan the main index.
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index c945b28..14d4e48 100644
--- a/src/backend/access/gin/ginxlog.c
+++ b/src/backend/access/gin/ginxlog.c
@@ -95,13 +95,13 @@ ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rda
 
 	if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), offset, false, false) == InvalidOffsetNumber)
 	{
-		RelFileNode node;
+		RelFileNodeBackend rnode;
 		ForkNumber	forknum;
 		BlockNumber blknum;
 
-		BufferGetTag(buffer, &node, &forknum, &blknum);
+		BufferGetTag(buffer, &rnode, &forknum, &blknum);
 		elog(ERROR, "failed to add item to index page in %u/%u/%u",
-			 node.spcNode, node.dbNode, node.relNode);
+			 rnode.node.spcNode, rnode.node.dbNode, rnode.node.relNode);
 	}
 }
 
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index e9ca4b8..d3ea072 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -677,7 +677,10 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace,
 		if (!xlocked)
 		{
 			LockBuffer(stack->buffer, GIST_SHARE);
-			gistcheckpage(state.r, stack->buffer);
+			if (stack->blkno == GIST_ROOT_BLKNO && GlobalTempRelationPageIsNotInitialized(state.r, BufferGetPage(stack->buffer)))
+				GISTInitBuffer(stack->buffer, F_LEAF);
+			else
+				gistcheckpage(state.r, stack->buffer);
 		}
 
 		stack->page = (Page) BufferGetPage(stack->buffer);
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 4e0c500..cced239 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -339,7 +339,10 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 	buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
 	LockBuffer(buffer, GIST_SHARE);
 	PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
-	gistcheckpage(scan->indexRelation, buffer);
+	if (pageItem->blkno == GIST_ROOT_BLKNO && GlobalTempRelationPageIsNotInitialized(r, BufferGetPage(buffer)))
+		GISTInitBuffer(buffer, F_LEAF);
+	else
+		gistcheckpage(scan->indexRelation, buffer);
 	page = BufferGetPage(buffer);
 	TestForOldSnapshot(scan->xs_snapshot, r, page);
 	opaque = GistPageGetOpaque(page);
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 9726020..c99701d 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -1028,7 +1028,7 @@ gistGetFakeLSN(Relation rel)
 {
 	static XLogRecPtr counter = FirstNormalUnloggedLSN;
 
-	if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+	if (RelationHasSessionScope(rel))
 	{
 		/*
 		 * Temporary relations are only accessible in our session, so a simple
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index defdc9b..35de5fa 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -75,13 +75,20 @@ _hash_getbuf(Relation rel, BlockNumber blkno, int access, int flags)
 
 	buf = ReadBuffer(rel, blkno);
 
-	if (access != HASH_NOLOCK)
-		LockBuffer(buf, access);
-
 	/* ref count and lock type are correct */
 
-	_hash_checkpage(rel, buf, flags);
-
+	if (blkno == HASH_METAPAGE && GlobalTempRelationPageIsNotInitialized(rel, BufferGetPage(buf)))
+	{
+		_hash_init(rel, 0, MAIN_FORKNUM);
+		if (access != HASH_NOLOCK)
+			LockBuffer(buf, access);
+	}
+	else
+	{
+		if (access != HASH_NOLOCK)
+			LockBuffer(buf, access);
+		_hash_checkpage(rel, buf, flags);
+	}
 	return buf;
 }
 
@@ -339,7 +346,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
 	bool		use_wal;
 
 	/* safety check */
-	if (RelationGetNumberOfBlocksInFork(rel, forkNum) != 0)
+	if (rel->rd_rel->relpersistence != RELPERSISTENCE_SESSION && RelationGetNumberOfBlocksInFork(rel, forkNum) != 0)
 		elog(ERROR, "cannot initialize non-empty hash index \"%s\"",
 			 RelationGetRelationName(rel));
 
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 9430994..181efde 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -444,7 +444,7 @@ heapgetpage(TableScanDesc sscan, BlockNumber page)
 			if (all_visible)
 				valid = true;
 			else
-				valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+				valid = HeapTupleSatisfiesVisibility(scan->rs_base.rs_rd, &loctup, snapshot, buffer);
 
 			CheckForSerializableConflictOut(valid, scan->rs_base.rs_rd,
 											&loctup, buffer, snapshot);
@@ -664,7 +664,8 @@ heapgettup(HeapScanDesc scan,
 				/*
 				 * if current tuple qualifies, return it.
 				 */
-				valid = HeapTupleSatisfiesVisibility(tuple,
+				valid = HeapTupleSatisfiesVisibility(scan->rs_base.rs_rd,
+													 tuple,
 													 snapshot,
 													 scan->rs_cbuf);
 
@@ -1474,7 +1475,7 @@ heap_fetch(Relation relation,
 	/*
 	 * check tuple visibility, then release lock
 	 */
-	valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);
+	valid = HeapTupleSatisfiesVisibility(relation, tuple, snapshot, buffer);
 
 	if (valid)
 		PredicateLockTuple(relation, tuple, snapshot);
@@ -1612,7 +1613,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
 			ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum);
 
 			/* If it's visible per the snapshot, we must return it */
-			valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
+			valid = HeapTupleSatisfiesVisibility(relation, heapTuple, snapshot, buffer);
 			CheckForSerializableConflictOut(valid, relation, heapTuple,
 											buffer, snapshot);
 			/* reset to original, non-redirected, tid */
@@ -1754,7 +1755,7 @@ heap_get_latest_tid(TableScanDesc sscan,
 		 * Check tuple visibility; if visible, set it as the new result
 		 * candidate.
 		 */
-		valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
+		valid = HeapTupleSatisfiesVisibility(relation, &tp, snapshot, buffer);
 		CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
 		if (valid)
 			*tid = ctid;
@@ -1851,6 +1852,14 @@ ReleaseBulkInsertStatePin(BulkInsertState bistate)
 }
 
 
+static TransactionId
+GetTransactionId(Relation relation)
+{
+	return relation->rd_rel->relpersistence == RELPERSISTENCE_SESSION && RecoveryInProgress()
+		? GetReplicaTransactionId()
+		: GetCurrentTransactionId();
+}
+
 /*
  *	heap_insert		- insert tuple into a heap
  *
@@ -1873,7 +1882,7 @@ void
 heap_insert(Relation relation, HeapTuple tup, CommandId cid,
 			int options, BulkInsertState bistate)
 {
-	TransactionId xid = GetCurrentTransactionId();
+	TransactionId xid = GetTransactionId(relation);
 	HeapTuple	heaptup;
 	Buffer		buffer;
 	Buffer		vmbuffer = InvalidBuffer;
@@ -2110,7 +2119,7 @@ void
 heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
 				  CommandId cid, int options, BulkInsertState bistate)
 {
-	TransactionId xid = GetCurrentTransactionId();
+	TransactionId xid = GetTransactionId(relation);
 	HeapTuple  *heaptuples;
 	int			i;
 	int			ndone;
@@ -2449,7 +2458,7 @@ heap_delete(Relation relation, ItemPointer tid,
 			TM_FailureData *tmfd, bool changingPart)
 {
 	TM_Result	result;
-	TransactionId xid = GetCurrentTransactionId();
+	TransactionId xid = GetTransactionId(relation);
 	ItemId		lp;
 	HeapTupleData tp;
 	Page		page;
@@ -2514,7 +2523,7 @@ heap_delete(Relation relation, ItemPointer tid,
 	tp.t_self = *tid;
 
 l1:
-	result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
+	result = HeapTupleSatisfiesUpdate(relation, &tp, cid, buffer);
 
 	if (result == TM_Invisible)
 	{
@@ -2633,7 +2642,7 @@ l1:
 	if (crosscheck != InvalidSnapshot && result == TM_Ok)
 	{
 		/* Perform additional check for transaction-snapshot mode RI updates */
-		if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
+		if (!HeapTupleSatisfiesVisibility(relation, &tp, crosscheck, buffer))
 			result = TM_Updated;
 	}
 
@@ -2900,7 +2909,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 			TM_FailureData *tmfd, LockTupleMode *lockmode)
 {
 	TM_Result	result;
-	TransactionId xid = GetCurrentTransactionId();
+	TransactionId xid = GetTransactionId(relation);
 	Bitmapset  *hot_attrs;
 	Bitmapset  *key_attrs;
 	Bitmapset  *id_attrs;
@@ -3070,7 +3079,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 l2:
 	checked_lockers = false;
 	locker_remains = false;
-	result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);
+	result = HeapTupleSatisfiesUpdate(relation, &oldtup, cid, buffer);
 
 	/* see below about the "no wait" case */
 	Assert(result != TM_BeingModified || wait);
@@ -3262,7 +3271,7 @@ l2:
 	if (crosscheck != InvalidSnapshot && result == TM_Ok)
 	{
 		/* Perform additional check for transaction-snapshot mode RI updates */
-		if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
+		if (!HeapTupleSatisfiesVisibility(relation, &oldtup, crosscheck, buffer))
 		{
 			result = TM_Updated;
 			Assert(!ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid));
@@ -4018,7 +4027,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
 	tuple->t_tableOid = RelationGetRelid(relation);
 
 l3:
-	result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
+	result = HeapTupleSatisfiesUpdate(relation, tuple, cid, *buffer);
 
 	if (result == TM_Invisible)
 	{
@@ -4193,7 +4202,7 @@ l3:
 					TM_Result	res;
 
 					res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
-												  GetCurrentTransactionId(),
+												  GetTransactionId(relation),
 												  mode);
 					if (res != TM_Ok)
 					{
@@ -4441,7 +4450,7 @@ l3:
 				TM_Result	res;
 
 				res = heap_lock_updated_tuple(relation, tuple, &t_ctid,
-											  GetCurrentTransactionId(),
+											  GetTransactionId(relation),
 											  mode);
 				if (res != TM_Ok)
 				{
@@ -4550,7 +4559,7 @@ failed:
 	 * state if multixact.c elogs.
 	 */
 	compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2,
-							  GetCurrentTransactionId(), mode, false,
+							  GetTransactionId(relation), mode, false,
 							  &xid, &new_infomask, &new_infomask2);
 
 	START_CRIT_SECTION();
@@ -5570,7 +5579,7 @@ heap_finish_speculative(Relation relation, ItemPointer tid)
 void
 heap_abort_speculative(Relation relation, ItemPointer tid)
 {
-	TransactionId xid = GetCurrentTransactionId();
+	TransactionId xid = GetTransactionId(relation);
 	ItemId		lp;
 	HeapTupleData tp;
 	Page		page;
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 09bc6fe..a189834 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -226,7 +226,8 @@ heapam_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot,
 	 * Caller should be holding pin, but not lock.
 	 */
 	LockBuffer(bslot->buffer, BUFFER_LOCK_SHARE);
-	res = HeapTupleSatisfiesVisibility(bslot->base.tuple, snapshot,
+
+	res = HeapTupleSatisfiesVisibility(rel, bslot->base.tuple, snapshot,
 									   bslot->buffer);
 	LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
 
@@ -671,6 +672,7 @@ heapam_relation_copy_data(Relation rel, const RelFileNode *newrnode)
 			 * init fork of an unlogged relation.
 			 */
 			if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
+				rel->rd_rel->relpersistence == RELPERSISTENCE_SESSION ||
 				(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
 				 forkNum == INIT_FORKNUM))
 				log_smgrcreate(newrnode, forkNum);
@@ -2160,7 +2162,7 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 			loctup.t_len = ItemIdGetLength(lp);
 			loctup.t_tableOid = scan->rs_rd->rd_id;
 			ItemPointerSet(&loctup.t_self, page, offnum);
-			valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
+			valid = HeapTupleSatisfiesVisibility(scan->rs_rd, &loctup, snapshot, buffer);
 			if (valid)
 			{
 				hscan->rs_vistuples[ntup++] = offnum;
@@ -2480,7 +2482,7 @@ SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer,
 	else
 	{
 		/* Otherwise, we have to check the tuple individually. */
-		return HeapTupleSatisfiesVisibility(tuple, scan->rs_snapshot,
+		return HeapTupleSatisfiesVisibility(scan->rs_rd, tuple, scan->rs_snapshot,
 											buffer);
 	}
 }
diff --git a/src/backend/access/heap/heapam_visibility.c b/src/backend/access/heap/heapam_visibility.c
index 537e681..3076f6a 100644
--- a/src/backend/access/heap/heapam_visibility.c
+++ b/src/backend/access/heap/heapam_visibility.c
@@ -77,6 +77,7 @@
 #include "utils/combocid.h"
 #include "utils/snapmgr.h"
 
+static bool TempTupleSatisfiesVisibility(HeapTuple htup, CommandId curcid, Buffer buffer);
 
 /*
  * SetHintBits()
@@ -454,7 +455,7 @@ HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
  *	test for it themselves.)
  */
 TM_Result
-HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
+HeapTupleSatisfiesUpdate(Relation relation, HeapTuple htup, CommandId curcid,
 						 Buffer buffer)
 {
 	HeapTupleHeader tuple = htup->t_data;
@@ -462,6 +463,13 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
 	Assert(ItemPointerIsValid(&htup->t_self));
 	Assert(htup->t_tableOid != InvalidOid);
 
+	if (relation->rd_rel->relpersistence == RELPERSISTENCE_SESSION && RecoveryInProgress())
+	{
+		AccessTempRelationAtReplica = true;
+		return TempTupleSatisfiesVisibility(htup, curcid, buffer) ? TM_Ok : TM_Invisible;
+	}
+	AccessTempRelationAtReplica = false;
+
 	if (!HeapTupleHeaderXminCommitted(tuple))
 	{
 		if (HeapTupleHeaderXminInvalid(tuple))
@@ -1677,6 +1685,59 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
 }
 
 /*
+ * TempTupleSatisfiesVisibility
+ *		True iff global temp table tuple is visible for the current transaction.
+ *
+ * Temporary tables are visible only for current backend, so there is no need to
+ * handle cases with tuples committed by other backends. We only need to exclude
+ * modifications done by aborted transactions or after start of table scan.
+ *
+ */
+static bool
+TempTupleSatisfiesVisibility(HeapTuple htup, CommandId curcid, Buffer buffer)
+{
+	HeapTupleHeader tuple = htup->t_data;
+	TransactionId xmin;
+	TransactionId xmax;
+
+	Assert(ItemPointerIsValid(&htup->t_self));
+	Assert(htup->t_tableOid != InvalidOid);
+
+	if (HeapTupleHeaderXminInvalid(tuple))
+		return false;
+
+	xmin = HeapTupleHeaderGetRawXmin(tuple);
+
+	if (IsReplicaTransactionAborted(xmin))
+		return false;
+
+	if (IsReplicaCurrentTransactionId(xmin)
+		&& HeapTupleHeaderGetCmin(tuple) >= curcid)
+	{
+		return false;	/* inserted after scan started */
+	}
+
+	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
+		return true;
+
+	if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))	/* not deleter */
+		return true;
+
+	xmax = (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+	    ? HeapTupleGetUpdateXid(tuple)
+		: HeapTupleHeaderGetRawXmax(tuple);
+
+	if (IsReplicaTransactionAborted(xmax))
+		return true; /* updating subtransaction aborted */
+
+	if (!IsReplicaCurrentTransactionId(xmax))
+		return false; /* updating transaction committed */
+
+	return (HeapTupleHeaderGetCmax(tuple) >= curcid);	/* updated after scan started */
+}
+
+
+/*
  * HeapTupleSatisfiesVisibility
  *		True iff heap tuple satisfies a time qual.
  *
@@ -1687,8 +1748,15 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
  *	if so, the indicated buffer is marked dirty.
  */
 bool
-HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)
+HeapTupleSatisfiesVisibility(Relation relation, HeapTuple tup, Snapshot snapshot, Buffer buffer)
 {
+	if (relation->rd_rel->relpersistence == RELPERSISTENCE_SESSION && RecoveryInProgress())
+	{
+		AccessTempRelationAtReplica = true;
+		return TempTupleSatisfiesVisibility(tup, snapshot->curcid, buffer);
+	}
+	AccessTempRelationAtReplica = false;
+
 	switch (snapshot->snapshot_type)
 	{
 		case SNAPSHOT_MVCC:
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 5962126..bdb6c95 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -763,7 +763,11 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access)
 		/* Read an existing block of the relation */
 		buf = ReadBuffer(rel, blkno);
 		LockBuffer(buf, access);
-		_bt_checkpage(rel, buf);
+		/* Session temporary relation may be not yet initialized for this backend. */
+		if (blkno == BTREE_METAPAGE && GlobalTempRelationPageIsNotInitialized(rel, BufferGetPage(buf)))
+			_bt_initmetapage(BufferGetPage(buf), P_NONE, 0);
+		else
+			_bt_checkpage(rel, buf);
 	}
 	else
 	{
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 45472db..a8497a2 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -106,6 +106,7 @@ spgGetCache(Relation index)
 		spgConfigIn in;
 		FmgrInfo   *procinfo;
 		Buffer		metabuffer;
+		Page        metapage;
 		SpGistMetaPageData *metadata;
 
 		cache = MemoryContextAllocZero(index->rd_indexcxt,
@@ -155,12 +156,32 @@ spgGetCache(Relation index)
 		metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
 		LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
 
-		metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
+		metapage = BufferGetPage(metabuffer);
+		metadata = SpGistPageGetMeta(metapage);
 
 		if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
-			elog(ERROR, "index \"%s\" is not an SP-GiST index",
-				 RelationGetRelationName(index));
+		{
+			if (GlobalTempRelationPageIsNotInitialized(index, metapage))
+			{
+				Buffer rootbuffer = ReadBuffer(index, SPGIST_ROOT_BLKNO);
+				Buffer nullbuffer = ReadBuffer(index, SPGIST_NULL_BLKNO);
+
+				SpGistInitMetapage(metapage);
+
+				LockBuffer(rootbuffer, BUFFER_LOCK_EXCLUSIVE);
+				SpGistInitPage(BufferGetPage(rootbuffer), SPGIST_LEAF);
+				MarkBufferDirty(rootbuffer);
+				UnlockReleaseBuffer(rootbuffer);
 
+				LockBuffer(nullbuffer, BUFFER_LOCK_EXCLUSIVE);
+				SpGistInitPage(BufferGetPage(nullbuffer), SPGIST_LEAF | SPGIST_NULLS);
+				MarkBufferDirty(nullbuffer);
+				UnlockReleaseBuffer(nullbuffer);
+			}
+			else
+				elog(ERROR, "index \"%s\" is not an SP-GiST index",
+					 RelationGetRelationName(index));
+		}
 		cache->lastUsedPages = metadata->lastUsedPages;
 
 		UnlockReleaseBuffer(metabuffer);
diff --git a/src/backend/access/transam/transam.c b/src/backend/access/transam/transam.c
index 365ddfb..bce9c4a 100644
--- a/src/backend/access/transam/transam.c
+++ b/src/backend/access/transam/transam.c
@@ -22,6 +22,7 @@
 #include "access/clog.h"
 #include "access/subtrans.h"
 #include "access/transam.h"
+#include "access/xact.h"
 #include "utils/snapmgr.h"
 
 /*
@@ -126,6 +127,9 @@ TransactionIdDidCommit(TransactionId transactionId)
 {
 	XidStatus	xidstatus;
 
+	if (AccessTempRelationAtReplica)
+		return !IsReplicaCurrentTransactionId(transactionId) && !IsReplicaTransactionAborted(transactionId);
+
 	xidstatus = TransactionLogFetch(transactionId);
 
 	/*
@@ -182,6 +186,9 @@ TransactionIdDidAbort(TransactionId transactionId)
 {
 	XidStatus	xidstatus;
 
+	if (AccessTempRelationAtReplica)
+		return IsReplicaTransactionAborted(transactionId);
+
 	xidstatus = TransactionLogFetch(transactionId);
 
 	/*
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 5b759ec..388faae 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -71,7 +71,7 @@ GetNewTransactionId(bool isSubXact)
 
 	/* safety check, we should never get this far in a HS standby */
 	if (RecoveryInProgress())
-		elog(ERROR, "cannot assign TransactionIds during recovery");
+   	    elog(ERROR, "cannot assign TransactionIds during recovery");
 
 	LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 1bbaeee..ab1bef9 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -192,6 +192,7 @@ typedef struct TransactionStateData
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
 	struct TransactionStateData *parent;	/* back link to parent */
+	TransactionId replicaTransactionId;    /* pseudo XID for inserting data in global temp tables at replica */
 } TransactionStateData;
 
 typedef TransactionStateData *TransactionState;
@@ -286,6 +287,12 @@ typedef struct XactCallbackItem
 
 static XactCallbackItem *Xact_callbacks = NULL;
 
+static TransactionId replicaTransIdCount = FirstNormalTransactionId;
+static TransactionId replicaTopTransId;
+static Bitmapset*    replicaAbortedXids;
+
+bool AccessTempRelationAtReplica;
+
 /*
  * List of add-on start- and end-of-subxact callbacks
  */
@@ -443,6 +450,48 @@ GetCurrentTransactionIdIfAny(void)
 }
 
 /*
+ * Transactions at replica can update only global temporary tables.
+ * Them are assigned backend-local XIDs which are independent from normal XIDs received from primary node.
+ * So tuples of temporary tables at replica requires special visibility rules.
+ *
+ * XIDs for such transactions at replica are created on demand (when tuple of temp table is updated).
+ * XID wrap-around and adjusting XID horizon is not supported. So number of such transactions at replica is
+ * limited by 2^32 and require up to 2^29 in-memory bitmap for marking aborted transactions.
+  */
+TransactionId
+GetReplicaTransactionId(void)
+{
+	TransactionState s = CurrentTransactionState;
+	if (!TransactionIdIsValid(s->replicaTransactionId))
+		s->replicaTransactionId = ++replicaTransIdCount;
+	return s->replicaTransactionId;
+}
+
+/*
+ * At replica transaction can update only temporary tables
+ * and them are assigned special XIDs (not related with normal XIDs received from primary node).
+ * As far as we see only own transaction it is not necessary to mark committed transactions.
+ * Only marking aborted ones is enough. All transactions which are not marked as aborted are treated as
+ * committed or self in-progress transactions.
+ */
+bool
+IsReplicaTransactionAborted(TransactionId xid)
+{
+	return bms_is_member(xid, replicaAbortedXids);
+}
+
+/*
+ * As far as XIDs for transactions at replica are generated individually for each backends,
+ * we can check that XID belongs to the current transaction or any of its subtransactions by
+ * just comparing it with XID of top transaction.
+ */
+bool
+IsReplicaCurrentTransactionId(TransactionId xid)
+{
+	return xid > replicaTopTransId;
+}
+
+/*
  *	GetTopFullTransactionId
  *
  * This will return the FullTransactionId of the main transaction, assigning
@@ -855,6 +904,9 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 {
 	TransactionState s;
 
+	if (AccessTempRelationAtReplica)
+		return IsReplicaCurrentTransactionId(xid);
+
 	/*
 	 * We always say that BootstrapTransactionId is "not my transaction ID"
 	 * even when it is (ie, during bootstrap).  Along with the fact that
@@ -1206,7 +1258,7 @@ static TransactionId
 RecordTransactionCommit(void)
 {
 	TransactionId xid = GetTopTransactionIdIfAny();
-	bool		markXidCommitted = TransactionIdIsValid(xid);
+	bool		markXidCommitted = TransactionIdIsNormal(xid);
 	TransactionId latestXid = InvalidTransactionId;
 	int			nrels;
 	RelFileNode *rels;
@@ -1624,7 +1676,7 @@ RecordTransactionAbort(bool isSubXact)
 	 * rels to delete (note that this routine is not responsible for actually
 	 * deleting 'em).  We cannot have any child XIDs, either.
 	 */
-	if (!TransactionIdIsValid(xid))
+	if (!TransactionIdIsNormal(xid))
 	{
 		/* Reset XactLastRecEnd until the next transaction writes something */
 		if (!isSubXact)
@@ -1892,6 +1944,8 @@ StartTransaction(void)
 	s = &TopTransactionStateData;
 	CurrentTransactionState = s;
 
+	replicaTopTransId = replicaTransIdCount;
+
 	Assert(!FullTransactionIdIsValid(XactTopFullTransactionId));
 
 	/* check the current transaction state */
@@ -1905,6 +1959,7 @@ StartTransaction(void)
 	 */
 	s->state = TRANS_START;
 	s->fullTransactionId = InvalidFullTransactionId;	/* until assigned */
+	s->replicaTransactionId = InvalidTransactionId;	/* until assigned */
 
 	/* Determine if statements are logged in this transaction */
 	xact_is_sampled = log_xact_sample_rate != 0 &&
@@ -2570,6 +2625,14 @@ AbortTransaction(void)
 	/* Prevent cancel/die interrupt while cleaning up */
 	HOLD_INTERRUPTS();
 
+	/* Mark transactions involved global temp table at replica as aborted */
+	if (TransactionIdIsValid(s->replicaTransactionId))
+	{
+		MemoryContext ctx = MemoryContextSwitchTo(TopMemoryContext);
+		replicaAbortedXids = bms_add_member(replicaAbortedXids, s->replicaTransactionId);
+		MemoryContextSwitchTo(ctx);
+	}
+
 	/* Make sure we have a valid memory context and resource owner */
 	AtAbort_Memory();
 	AtAbort_ResourceOwner();
@@ -2991,6 +3054,9 @@ CommitTransactionCommand(void)
 			 * and then clean up.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			if (GetCurrentTransactionIdIfAny() == FrozenTransactionId)
+				elog(FATAL, "Transaction is aborted at standby");
+
 			AbortTransaction();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
@@ -4856,6 +4922,14 @@ AbortSubTransaction(void)
 	/* Prevent cancel/die interrupt while cleaning up */
 	HOLD_INTERRUPTS();
 
+	/* Mark transactions involved global temp table at replica as aborted */
+	if (TransactionIdIsValid(s->replicaTransactionId))
+	{
+		MemoryContext ctx = MemoryContextSwitchTo(TopMemoryContext);
+		replicaAbortedXids = bms_add_member(replicaAbortedXids, s->replicaTransactionId);
+		MemoryContextSwitchTo(ctx);
+	}
+
 	/* Make sure we have a valid memory context and resource owner */
 	AtSubAbort_Memory();
 	AtSubAbort_ResourceOwner();
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 3ec67d4..edec8ca 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -213,6 +213,7 @@ void
 XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
 {
 	registered_buffer *regbuf;
+	RelFileNodeBackend rnode;
 
 	/* NO_IMAGE doesn't make sense with FORCE_IMAGE */
 	Assert(!((flags & REGBUF_FORCE_IMAGE) && (flags & (REGBUF_NO_IMAGE))));
@@ -227,7 +228,8 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
 
 	regbuf = &registered_buffers[block_id];
 
-	BufferGetTag(buffer, &regbuf->rnode, &regbuf->forkno, &regbuf->block);
+	BufferGetTag(buffer, &rnode, &regbuf->forkno, &regbuf->block);
+	regbuf->rnode = rnode.node;
 	regbuf->page = BufferGetPage(buffer);
 	regbuf->flags = flags;
 	regbuf->rdata_tail = (XLogRecData *) &regbuf->rdata_head;
@@ -919,7 +921,7 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
 		int			flags;
 		PGAlignedBlock copied_buffer;
 		char	   *origdata = (char *) BufferGetBlock(buffer);
-		RelFileNode rnode;
+		RelFileNodeBackend rnode;
 		ForkNumber	forkno;
 		BlockNumber blkno;
 
@@ -948,7 +950,7 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
 			flags |= REGBUF_STANDARD;
 
 		BufferGetTag(buffer, &rnode, &forkno, &blkno);
-		XLogRegisterBlock(0, &rnode, forkno, blkno, copied_buffer.data, flags);
+		XLogRegisterBlock(0, &rnode.node, forkno, blkno, copied_buffer.data, flags);
 
 		recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI_FOR_HINT);
 	}
@@ -1009,7 +1011,7 @@ XLogRecPtr
 log_newpage_buffer(Buffer buffer, bool page_std)
 {
 	Page		page = BufferGetPage(buffer);
-	RelFileNode rnode;
+	RelFileNodeBackend rnode;
 	ForkNumber	forkNum;
 	BlockNumber blkno;
 
@@ -1018,7 +1020,7 @@ log_newpage_buffer(Buffer buffer, bool page_std)
 
 	BufferGetTag(buffer, &rnode, &forkNum, &blkno);
 
-	return log_newpage(&rnode, forkNum, blkno, page, page_std);
+	return log_newpage(&rnode.node, forkNum, blkno, page, page_std);
 }
 
 /*
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index a065419..8814afb 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -409,6 +409,9 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 		case RELPERSISTENCE_TEMP:
 			backend = BackendIdForTempRelations();
 			break;
+		case RELPERSISTENCE_SESSION:
+			backend = BackendIdForSessionRelations();
+			break;
 		case RELPERSISTENCE_UNLOGGED:
 		case RELPERSISTENCE_PERMANENT:
 			backend = InvalidBackendId;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 99ae159..24b2438 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -3612,7 +3612,7 @@ reindex_relation(Oid relid, int flags, int options)
 		if (flags & REINDEX_REL_FORCE_INDEXES_UNLOGGED)
 			persistence = RELPERSISTENCE_UNLOGGED;
 		else if (flags & REINDEX_REL_FORCE_INDEXES_PERMANENT)
-			persistence = RELPERSISTENCE_PERMANENT;
+			persistence = rel->rd_rel->relpersistence == RELPERSISTENCE_SESSION ? RELPERSISTENCE_SESSION : RELPERSISTENCE_PERMANENT;
 		else
 			persistence = rel->rd_rel->relpersistence;
 
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 3cc886f..a111ddc 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -93,6 +93,10 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 			backend = InvalidBackendId;
 			needs_wal = false;
 			break;
+		case RELPERSISTENCE_SESSION:
+			backend = BackendIdForSessionRelations();
+			needs_wal = false;
+			break;
 		case RELPERSISTENCE_PERMANENT:
 			backend = InvalidBackendId;
 			needs_wal = true;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index cedb4ee..d11c5b3 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1400,7 +1400,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
 	 */
 	if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
 		reindex_flags |= REINDEX_REL_FORCE_INDEXES_UNLOGGED;
-	else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
+	else if (newrelpersistence != RELPERSISTENCE_TEMP)
 		reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT;
 
 	/* Report that we are now reindexing relations */
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 0960b33..6c3998f 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -94,7 +94,7 @@ static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
  */
 static SeqTableData *last_used_seq = NULL;
 
-static void fill_seq_with_data(Relation rel, HeapTuple tuple);
+static void fill_seq_with_data(Relation rel, HeapTuple tuple, Buffer buf);
 static Relation lock_and_open_sequence(SeqTable seq);
 static void create_seq_hashtable(void);
 static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
@@ -222,7 +222,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
 
 	/* now initialize the sequence's data */
 	tuple = heap_form_tuple(tupDesc, value, null);
-	fill_seq_with_data(rel, tuple);
+	fill_seq_with_data(rel, tuple, InvalidBuffer);
 
 	/* process OWNED BY if given */
 	if (owned_by)
@@ -327,7 +327,7 @@ ResetSequence(Oid seq_relid)
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seq_rel, tuple, InvalidBuffer);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
@@ -340,18 +340,21 @@ ResetSequence(Oid seq_relid)
  * Initialize a sequence's relation with the specified tuple as content
  */
 static void
-fill_seq_with_data(Relation rel, HeapTuple tuple)
+fill_seq_with_data(Relation rel, HeapTuple tuple, Buffer buf)
 {
-	Buffer		buf;
 	Page		page;
 	sequence_magic *sm;
 	OffsetNumber offnum;
+	bool lockBuffer = false;
 
 	/* Initialize first page of relation with special magic number */
 
-	buf = ReadBuffer(rel, P_NEW);
-	Assert(BufferGetBlockNumber(buf) == 0);
-
+	if (buf == InvalidBuffer)
+	{
+		buf = ReadBuffer(rel, P_NEW);
+		Assert(BufferGetBlockNumber(buf) == 0);
+		lockBuffer = true;
+	}
 	page = BufferGetPage(buf);
 
 	PageInit(page, BufferGetPageSize(buf), sizeof(sequence_magic));
@@ -360,7 +363,8 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 
 	/* Now insert sequence tuple */
 
-	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+	if (lockBuffer)
+		LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
 	/*
 	 * Since VACUUM does not process sequences, we have to force the tuple to
@@ -410,7 +414,8 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 
 	END_CRIT_SECTION();
 
-	UnlockReleaseBuffer(buf);
+	if (lockBuffer)
+		UnlockReleaseBuffer(buf);
 }
 
 /*
@@ -502,7 +507,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
 		/*
 		 * Insert the modified tuple into the new storage file.
 		 */
-		fill_seq_with_data(seqrel, newdatatuple);
+		fill_seq_with_data(seqrel, newdatatuple, InvalidBuffer);
 	}
 
 	/* process OWNED BY if given */
@@ -1178,6 +1183,17 @@ read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
 	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
 
 	page = BufferGetPage(*buf);
+	if (GlobalTempRelationPageIsNotInitialized(rel, page))
+	{
+		/* Initialize sequence for global temporary tables */
+		Datum		value[SEQ_COL_LASTCOL] = {0};
+		bool		null[SEQ_COL_LASTCOL] = {false};
+		HeapTuple tuple;
+		value[SEQ_COL_LASTVAL-1] = Int64GetDatumFast(1); /* start sequence with 1 */
+		tuple = heap_form_tuple(RelationGetDescr(rel), value, null);
+		fill_seq_with_data(rel, tuple, *buf);
+	}
+
 	sm = (sequence_magic *) PageGetSpecialPointer(page);
 
 	if (sm->magic != SEQ_MAGIC)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fb2be10..a31f775 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -586,7 +586,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	 * Check consistency of arguments
 	 */
 	if (stmt->oncommit != ONCOMMIT_NOOP
-		&& stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
+		&& !IsLocalRelpersistence(stmt->relation->relpersistence))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 				 errmsg("ON COMMIT can only be used on temporary tables")));
@@ -1772,7 +1772,8 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
 		 * table or the current physical file to be thrown away anyway.
 		 */
 		if (rel->rd_createSubid == mySubid ||
-			rel->rd_newRelfilenodeSubid == mySubid)
+			rel->rd_newRelfilenodeSubid == mySubid ||
+			rel->rd_rel->relpersistence == RELPERSISTENCE_SESSION)
 		{
 			/* Immediate, non-rollbackable truncation is OK */
 			heap_truncate_one_rel(rel);
@@ -7678,6 +7679,12 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 						 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
 			break;
+		case RELPERSISTENCE_SESSION:
+			if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_SESSION)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("constraints on session tables may reference only session tables")));
+			break;
 		case RELPERSISTENCE_TEMP:
 			if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
 				ereport(ERROR,
@@ -14082,6 +14089,13 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
 							RelationGetRelationName(rel)),
 					 errtable(rel)));
 			break;
+		case RELPERSISTENCE_SESSION:
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+					 errmsg("cannot change logged status of session table \"%s\"",
+							RelationGetRelationName(rel)),
+					 errtable(rel)));
+			break;
 		case RELPERSISTENCE_PERMANENT:
 			if (toLogged)
 				/* nothing to do */
@@ -14569,14 +14583,7 @@ PreCommit_on_commit_actions(void)
 				/* Do nothing (there shouldn't be such entries, actually) */
 				break;
 			case ONCOMMIT_DELETE_ROWS:
-
-				/*
-				 * If this transaction hasn't accessed any temporary
-				 * relations, we can skip truncating ON COMMIT DELETE ROWS
-				 * tables, as they must still be empty.
-				 */
-				if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
-					oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
+				oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
 				break;
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index dbd7dd9..efe6f21 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -788,6 +788,9 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 		if (isTempNamespace(get_rel_namespace(rte->relid)))
 			continue;
 
+		if (get_rel_persistence(rte->relid) == RELPERSISTENCE_SESSION)
+			continue;
+
 		PreventCommandIfReadOnly(CreateCommandTag((Node *) plannedstmt));
 	}
 
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 98e9948..1a9170b 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -124,7 +124,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	relation = table_open(relationObjectId, NoLock);
 
 	/* Temporary and unlogged relations are inaccessible during recovery. */
-	if (!RelationNeedsWAL(relation) && RecoveryInProgress())
+	if (!RelationNeedsWAL(relation) && RecoveryInProgress() && relation->rd_rel->relpersistence != RELPERSISTENCE_SESSION)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot access temporary or unlogged relations during recovery")));
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c97bb36..f9b2000 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3265,20 +3265,11 @@ OptTemp:	TEMPORARY					{ $$ = RELPERSISTENCE_TEMP; }
 			| TEMP						{ $$ = RELPERSISTENCE_TEMP; }
 			| LOCAL TEMPORARY			{ $$ = RELPERSISTENCE_TEMP; }
 			| LOCAL TEMP				{ $$ = RELPERSISTENCE_TEMP; }
-			| GLOBAL TEMPORARY
-				{
-					ereport(WARNING,
-							(errmsg("GLOBAL is deprecated in temporary table creation"),
-							 parser_errposition(@1)));
-					$$ = RELPERSISTENCE_TEMP;
-				}
-			| GLOBAL TEMP
-				{
-					ereport(WARNING,
-							(errmsg("GLOBAL is deprecated in temporary table creation"),
-							 parser_errposition(@1)));
-					$$ = RELPERSISTENCE_TEMP;
-				}
+			| GLOBAL TEMPORARY          { $$ = RELPERSISTENCE_SESSION; }
+			| GLOBAL TEMP               { $$ = RELPERSISTENCE_SESSION; }
+			| SESSION                   { $$ = RELPERSISTENCE_SESSION; }
+			| SESSION TEMPORARY         { $$ = RELPERSISTENCE_SESSION; }
+			| SESSION TEMP              { $$ = RELPERSISTENCE_SESSION; }
 			| UNLOGGED					{ $$ = RELPERSISTENCE_UNLOGGED; }
 			| /*EMPTY*/					{ $$ = RELPERSISTENCE_PERMANENT; }
 		;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index a9b2f8b..2f261b9 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -437,6 +437,14 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
 	seqstmt->options = seqoptions;
 
 	/*
+	 * Why we should not always use persistence of parent table?
+	 * Although it is prohibited to have unlogged sequences,
+	 * unlogged tables with SERIAL fields are accepted!
+	 */
+	if (cxt->relation->relpersistence != RELPERSISTENCE_UNLOGGED)
+		seqstmt->sequence->relpersistence = cxt->relation->relpersistence;
+
+	/*
 	 * If a sequence data type was specified, add it to the options.  Prepend
 	 * to the list rather than append; in case a user supplied their own AS
 	 * clause, the "redundant options" error will point to their occurrence,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 073f313..5760a9c 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2154,7 +2154,7 @@ do_autovacuum(void)
 		/*
 		 * We cannot safely process other backends' temp tables, so skip 'em.
 		 */
-		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+		if (IsLocalRelpersistence(classForm->relpersistence))
 			continue;
 
 		relid = classForm->oid;
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index e8ffa04..2004d2f 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -3483,6 +3483,7 @@ ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data,
 {
 	ReorderBufferTupleCidKey key;
 	ReorderBufferTupleCidEnt *ent;
+	RelFileNodeBackend rnode;
 	ForkNumber	forkno;
 	BlockNumber blockno;
 	bool		updated_mapping = false;
@@ -3496,7 +3497,8 @@ ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data,
 	 * get relfilenode from the buffer, no convenient way to access it other
 	 * than that.
 	 */
-	BufferGetTag(buffer, &key.relnode, &forkno, &blockno);
+	BufferGetTag(buffer, &rnode, &forkno, &blockno);
+	key.relnode = rnode.node;
 
 	/* tuples can only be in the main fork */
 	Assert(forkno == MAIN_FORKNUM);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 6f3a402..76ce953 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -556,7 +556,7 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		int			buf_id;
 
 		/* create a tag so we can lookup the buffer */
-		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_rnode.node,
+		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_rnode,
 					   forkNum, blockNum);
 
 		/* determine its hash code and partition lock ID */
@@ -710,7 +710,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	Block		bufBlock;
 	bool		found;
 	bool		isExtend;
-	bool		isLocalBuf = SmgrIsTemp(smgr);
+	bool		isLocalBuf = SmgrIsTemp(smgr) && relpersistence == RELPERSISTENCE_TEMP;
 
 	*hit = false;
 
@@ -1010,7 +1010,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	uint32		buf_state;
 
 	/* create a tag so we can lookup the buffer */
-	INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
+	INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
 
 	/* determine its hash code and partition lock ID */
 	newHash = BufTableHashCode(&newTag);
@@ -1532,7 +1532,8 @@ ReleaseAndReadBuffer(Buffer buffer,
 		{
 			bufHdr = GetLocalBufferDescriptor(-buffer - 1);
 			if (bufHdr->tag.blockNum == blockNum &&
-				RelFileNodeEquals(bufHdr->tag.rnode, relation->rd_node) &&
+				RelFileNodeEquals(bufHdr->tag.rnode.node, relation->rd_node) &&
+				bufHdr->tag.rnode.backend == relation->rd_backend &&
 				bufHdr->tag.forkNum == forkNum)
 				return buffer;
 			ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer);
@@ -1543,7 +1544,8 @@ ReleaseAndReadBuffer(Buffer buffer,
 			bufHdr = GetBufferDescriptor(buffer - 1);
 			/* we have pin, so it's ok to examine tag without spinlock */
 			if (bufHdr->tag.blockNum == blockNum &&
-				RelFileNodeEquals(bufHdr->tag.rnode, relation->rd_node) &&
+				RelFileNodeEquals(bufHdr->tag.rnode.node, relation->rd_node) &&
+				bufHdr->tag.rnode.backend == relation->rd_backend &&
 				bufHdr->tag.forkNum == forkNum)
 				return buffer;
 			UnpinBuffer(bufHdr, true);
@@ -1845,8 +1847,8 @@ BufferSync(int flags)
 
 			item = &CkptBufferIds[num_to_scan++];
 			item->buf_id = buf_id;
-			item->tsId = bufHdr->tag.rnode.spcNode;
-			item->relNode = bufHdr->tag.rnode.relNode;
+			item->tsId = bufHdr->tag.rnode.node.spcNode;
+			item->relNode = bufHdr->tag.rnode.node.relNode;
 			item->forkNum = bufHdr->tag.forkNum;
 			item->blockNum = bufHdr->tag.blockNum;
 		}
@@ -2559,7 +2561,7 @@ PrintBufferLeakWarning(Buffer buffer)
 	}
 
 	/* theoretically we should lock the bufhdr here */
-	path = relpathbackend(buf->tag.rnode, backend, buf->tag.forkNum);
+	path = relpathbackend(buf->tag.rnode.node, backend, buf->tag.forkNum);
 	buf_state = pg_atomic_read_u32(&buf->state);
 	elog(WARNING,
 		 "buffer refcount leak: [%03d] "
@@ -2631,7 +2633,7 @@ BufferGetBlockNumber(Buffer buffer)
  *		a buffer.
  */
 void
-BufferGetTag(Buffer buffer, RelFileNode *rnode, ForkNumber *forknum,
+BufferGetTag(Buffer buffer, RelFileNodeBackend *rnode, ForkNumber *forknum,
 			 BlockNumber *blknum)
 {
 	BufferDesc *bufHdr;
@@ -2696,7 +2698,7 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
 
 	/* Find smgr relation for buffer */
 	if (reln == NULL)
-		reln = smgropen(buf->tag.rnode, InvalidBackendId);
+		reln = smgropen(buf->tag.rnode.node, buf->tag.rnode.backend);
 
 	TRACE_POSTGRESQL_BUFFER_FLUSH_START(buf->tag.forkNum,
 										buf->tag.blockNum,
@@ -2930,7 +2932,7 @@ DropRelFileNodeBuffers(RelFileNodeBackend rnode, ForkNumber forkNum,
 	int			i;
 
 	/* If it's a local relation, it's localbuf.c's problem. */
-	if (RelFileNodeBackendIsTemp(rnode))
+	if (RelFileNodeBackendIsLocalTemp(rnode))
 	{
 		if (rnode.backend == MyBackendId)
 			DropRelFileNodeLocalBuffers(rnode.node, forkNum, firstDelBlock);
@@ -2958,11 +2960,11 @@ DropRelFileNodeBuffers(RelFileNodeBackend rnode, ForkNumber forkNum,
 		 * We could check forkNum and blockNum as well as the rnode, but the
 		 * incremental win from doing so seems small.
 		 */
-		if (!RelFileNodeEquals(bufHdr->tag.rnode, rnode.node))
+		if (!RelFileNodeBackendEquals(bufHdr->tag.rnode, rnode))
 			continue;
 
 		buf_state = LockBufHdr(bufHdr);
-		if (RelFileNodeEquals(bufHdr->tag.rnode, rnode.node) &&
+		if (RelFileNodeBackendEquals(bufHdr->tag.rnode, rnode) &&
 			bufHdr->tag.forkNum == forkNum &&
 			bufHdr->tag.blockNum >= firstDelBlock)
 			InvalidateBuffer(bufHdr);	/* releases spinlock */
@@ -2985,24 +2987,24 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 {
 	int			i,
 				n = 0;
-	RelFileNode *nodes;
+	RelFileNodeBackend *nodes;
 	bool		use_bsearch;
 
 	if (nnodes == 0)
 		return;
 
-	nodes = palloc(sizeof(RelFileNode) * nnodes);	/* non-local relations */
+	nodes = palloc(sizeof(RelFileNodeBackend) * nnodes);	/* non-local relations */
 
 	/* If it's a local relation, it's localbuf.c's problem. */
 	for (i = 0; i < nnodes; i++)
 	{
-		if (RelFileNodeBackendIsTemp(rnodes[i]))
+		if (RelFileNodeBackendIsLocalTemp(rnodes[i]))
 		{
 			if (rnodes[i].backend == MyBackendId)
 				DropRelFileNodeAllLocalBuffers(rnodes[i].node);
 		}
 		else
-			nodes[n++] = rnodes[i].node;
+			nodes[n++] = rnodes[i];
 	}
 
 	/*
@@ -3025,11 +3027,11 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 
 	/* sort the list of rnodes if necessary */
 	if (use_bsearch)
-		pg_qsort(nodes, n, sizeof(RelFileNode), rnode_comparator);
+		pg_qsort(nodes, n, sizeof(RelFileNodeBackend), rnode_comparator);
 
 	for (i = 0; i < NBuffers; i++)
 	{
-		RelFileNode *rnode = NULL;
+		RelFileNodeBackend *rnode = NULL;
 		BufferDesc *bufHdr = GetBufferDescriptor(i);
 		uint32		buf_state;
 
@@ -3044,7 +3046,7 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 
 			for (j = 0; j < n; j++)
 			{
-				if (RelFileNodeEquals(bufHdr->tag.rnode, nodes[j]))
+				if (RelFileNodeBackendEquals(bufHdr->tag.rnode, nodes[j]))
 				{
 					rnode = &nodes[j];
 					break;
@@ -3054,7 +3056,7 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 		else
 		{
 			rnode = bsearch((const void *) &(bufHdr->tag.rnode),
-							nodes, n, sizeof(RelFileNode),
+							nodes, n, sizeof(RelFileNodeBackend),
 							rnode_comparator);
 		}
 
@@ -3063,7 +3065,7 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 			continue;
 
 		buf_state = LockBufHdr(bufHdr);
-		if (RelFileNodeEquals(bufHdr->tag.rnode, (*rnode)))
+		if (RelFileNodeBackendEquals(bufHdr->tag.rnode, (*rnode)))
 			InvalidateBuffer(bufHdr);	/* releases spinlock */
 		else
 			UnlockBufHdr(bufHdr, buf_state);
@@ -3102,11 +3104,11 @@ DropDatabaseBuffers(Oid dbid)
 		 * As in DropRelFileNodeBuffers, an unlocked precheck should be safe
 		 * and saves some cycles.
 		 */
-		if (bufHdr->tag.rnode.dbNode != dbid)
+		if (bufHdr->tag.rnode.node.dbNode != dbid)
 			continue;
 
 		buf_state = LockBufHdr(bufHdr);
-		if (bufHdr->tag.rnode.dbNode == dbid)
+		if (bufHdr->tag.rnode.node.dbNode == dbid)
 			InvalidateBuffer(bufHdr);	/* releases spinlock */
 		else
 			UnlockBufHdr(bufHdr, buf_state);
@@ -3136,7 +3138,7 @@ PrintBufferDescs(void)
 			 "[%02d] (freeNext=%d, rel=%s, "
 			 "blockNum=%u, flags=0x%x, refcount=%u %d)",
 			 i, buf->freeNext,
-			 relpathbackend(buf->tag.rnode, InvalidBackendId, buf->tag.forkNum),
+			 relpath(buf->tag.rnode, buf->tag.forkNum),
 			 buf->tag.blockNum, buf->flags,
 			 buf->refcount, GetPrivateRefCount(b));
 	}
@@ -3204,7 +3206,8 @@ FlushRelationBuffers(Relation rel)
 			uint32		buf_state;
 
 			bufHdr = GetLocalBufferDescriptor(i);
-			if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) &&
+			if (RelFileNodeEquals(bufHdr->tag.rnode.node, rel->rd_node) &&
+				bufHdr->tag.rnode.backend == rel->rd_backend &&
 				((buf_state = pg_atomic_read_u32(&bufHdr->state)) &
 				 (BM_VALID | BM_DIRTY)) == (BM_VALID | BM_DIRTY))
 			{
@@ -3251,13 +3254,15 @@ FlushRelationBuffers(Relation rel)
 		 * As in DropRelFileNodeBuffers, an unlocked precheck should be safe
 		 * and saves some cycles.
 		 */
-		if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node))
+		if (!RelFileNodeEquals(bufHdr->tag.rnode.node, rel->rd_node)
+			|| bufHdr->tag.rnode.backend != rel->rd_backend)
 			continue;
 
 		ReservePrivateRefCountEntry();
 
 		buf_state = LockBufHdr(bufHdr);
-		if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) &&
+		if (RelFileNodeEquals(bufHdr->tag.rnode.node, rel->rd_node) &&
+			bufHdr->tag.rnode.backend == rel->rd_backend &&
 			(buf_state & (BM_VALID | BM_DIRTY)) == (BM_VALID | BM_DIRTY))
 		{
 			PinBuffer_Locked(bufHdr);
@@ -3305,13 +3310,13 @@ FlushDatabaseBuffers(Oid dbid)
 		 * As in DropRelFileNodeBuffers, an unlocked precheck should be safe
 		 * and saves some cycles.
 		 */
-		if (bufHdr->tag.rnode.dbNode != dbid)
+		if (bufHdr->tag.rnode.node.dbNode != dbid)
 			continue;
 
 		ReservePrivateRefCountEntry();
 
 		buf_state = LockBufHdr(bufHdr);
-		if (bufHdr->tag.rnode.dbNode == dbid &&
+		if (bufHdr->tag.rnode.node.dbNode == dbid &&
 			(buf_state & (BM_VALID | BM_DIRTY)) == (BM_VALID | BM_DIRTY))
 		{
 			PinBuffer_Locked(bufHdr);
@@ -4051,7 +4056,7 @@ AbortBufferIO(void)
 				/* Buffer is pinned, so we can read tag without spinlock */
 				char	   *path;
 
-				path = relpathperm(buf->tag.rnode, buf->tag.forkNum);
+				path = relpath(buf->tag.rnode, buf->tag.forkNum);
 				ereport(WARNING,
 						(errcode(ERRCODE_IO_ERROR),
 						 errmsg("could not write block %u of %s",
@@ -4075,7 +4080,7 @@ shared_buffer_write_error_callback(void *arg)
 	/* Buffer is pinned, so we can read the tag without locking the spinlock */
 	if (bufHdr != NULL)
 	{
-		char	   *path = relpathperm(bufHdr->tag.rnode, bufHdr->tag.forkNum);
+		char	   *path = relpath(bufHdr->tag.rnode, bufHdr->tag.forkNum);
 
 		errcontext("writing block %u of relation %s",
 				   bufHdr->tag.blockNum, path);
@@ -4093,7 +4098,7 @@ local_buffer_write_error_callback(void *arg)
 
 	if (bufHdr != NULL)
 	{
-		char	   *path = relpathbackend(bufHdr->tag.rnode, MyBackendId,
+		char	   *path = relpathbackend(bufHdr->tag.rnode.node, MyBackendId,
 										  bufHdr->tag.forkNum);
 
 		errcontext("writing block %u of relation %s",
@@ -4108,22 +4113,27 @@ local_buffer_write_error_callback(void *arg)
 static int
 rnode_comparator(const void *p1, const void *p2)
 {
-	RelFileNode n1 = *(const RelFileNode *) p1;
-	RelFileNode n2 = *(const RelFileNode *) p2;
+	RelFileNodeBackend n1 = *(const RelFileNodeBackend *) p1;
+	RelFileNodeBackend n2 = *(const RelFileNodeBackend *) p2;
 
-	if (n1.relNode < n2.relNode)
+	if (n1.node.relNode < n2.node.relNode)
 		return -1;
-	else if (n1.relNode > n2.relNode)
+	else if (n1.node.relNode > n2.node.relNode)
 		return 1;
 
-	if (n1.dbNode < n2.dbNode)
+	if (n1.node.dbNode < n2.node.dbNode)
 		return -1;
-	else if (n1.dbNode > n2.dbNode)
+	else if (n1.node.dbNode > n2.node.dbNode)
 		return 1;
 
-	if (n1.spcNode < n2.spcNode)
+	if (n1.node.spcNode < n2.node.spcNode)
 		return -1;
-	else if (n1.spcNode > n2.spcNode)
+	else if (n1.node.spcNode > n2.node.spcNode)
+		return 1;
+
+	if (n1.backend < n2.backend)
+		return -1;
+	else if (n1.backend > n2.backend)
 		return 1;
 	else
 		return 0;
@@ -4359,7 +4369,7 @@ IssuePendingWritebacks(WritebackContext *context)
 			next = &context->pending_writebacks[i + ahead + 1];
 
 			/* different file, stop */
-			if (!RelFileNodeEquals(cur->tag.rnode, next->tag.rnode) ||
+			if (!RelFileNodeBackendEquals(cur->tag.rnode, next->tag.rnode) ||
 				cur->tag.forkNum != next->tag.forkNum)
 				break;
 
@@ -4378,7 +4388,7 @@ IssuePendingWritebacks(WritebackContext *context)
 		i += ahead;
 
 		/* and finally tell the kernel to write the data to storage */
-		reln = smgropen(tag.rnode, InvalidBackendId);
+		reln = smgropen(tag.rnode.node, tag.rnode.backend);
 		smgrwriteback(reln, tag.forkNum, tag.blockNum, nblocks);
 	}
 
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index f5f6a29..6bd5ecb 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -68,7 +68,7 @@ LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum,
 	BufferTag	newTag;			/* identity of requested block */
 	LocalBufferLookupEnt *hresult;
 
-	INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
+	INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
 
 	/* Initialize local buffers if first request in this session */
 	if (LocalBufHash == NULL)
@@ -111,7 +111,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 	bool		found;
 	uint32		buf_state;
 
-	INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
+	INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
 
 	/* Initialize local buffers if first request in this session */
 	if (LocalBufHash == NULL)
@@ -209,7 +209,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 		Page		localpage = (char *) LocalBufHdrGetBlock(bufHdr);
 
 		/* Find smgr relation for buffer */
-		oreln = smgropen(bufHdr->tag.rnode, MyBackendId);
+		oreln = smgropen(bufHdr->tag.rnode.node, MyBackendId);
 
 		PageSetChecksumInplace(localpage, bufHdr->tag.blockNum);
 
@@ -331,14 +331,14 @@ DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum,
 		buf_state = pg_atomic_read_u32(&bufHdr->state);
 
 		if ((buf_state & BM_TAG_VALID) &&
-			RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
+			RelFileNodeEquals(bufHdr->tag.rnode.node, rnode) &&
 			bufHdr->tag.forkNum == forkNum &&
 			bufHdr->tag.blockNum >= firstDelBlock)
 		{
 			if (LocalRefCount[i] != 0)
 				elog(ERROR, "block %u of %s is still referenced (local %u)",
 					 bufHdr->tag.blockNum,
-					 relpathbackend(bufHdr->tag.rnode, MyBackendId,
+					 relpathbackend(bufHdr->tag.rnode.node, MyBackendId,
 									bufHdr->tag.forkNum),
 					 LocalRefCount[i]);
 			/* Remove entry from hashtable */
@@ -377,12 +377,12 @@ DropRelFileNodeAllLocalBuffers(RelFileNode rnode)
 		buf_state = pg_atomic_read_u32(&bufHdr->state);
 
 		if ((buf_state & BM_TAG_VALID) &&
-			RelFileNodeEquals(bufHdr->tag.rnode, rnode))
+			RelFileNodeEquals(bufHdr->tag.rnode.node, rnode))
 		{
 			if (LocalRefCount[i] != 0)
 				elog(ERROR, "block %u of %s is still referenced (local %u)",
 					 bufHdr->tag.blockNum,
-					 relpathbackend(bufHdr->tag.rnode, MyBackendId,
+					 relpathbackend(bufHdr->tag.rnode.node, MyBackendId,
 									bufHdr->tag.forkNum),
 					 LocalRefCount[i]);
 			/* Remove entry from hashtable */
diff --git a/src/backend/storage/freespace/fsmpage.c b/src/backend/storage/freespace/fsmpage.c
index cf7f03f..65eb422 100644
--- a/src/backend/storage/freespace/fsmpage.c
+++ b/src/backend/storage/freespace/fsmpage.c
@@ -268,13 +268,13 @@ restart:
 			 *
 			 * Fix the corruption and restart.
 			 */
-			RelFileNode rnode;
+			RelFileNodeBackend rnode;
 			ForkNumber	forknum;
 			BlockNumber blknum;
 
 			BufferGetTag(buf, &rnode, &forknum, &blknum);
 			elog(DEBUG1, "fixing corrupt FSM block %u, relation %u/%u/%u",
-				 blknum, rnode.spcNode, rnode.dbNode, rnode.relNode);
+				 blknum, rnode.node.spcNode, rnode.node.dbNode, rnode.node.relNode);
 
 			/* make sure we hold an exclusive lock */
 			if (!exclusive_lock_held)
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index fadab62..055ec6b 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -994,6 +994,9 @@ TransactionIdIsInProgress(TransactionId xid)
 	int			i,
 				j;
 
+	if (AccessTempRelationAtReplica)
+		return IsReplicaCurrentTransactionId(xid) && !IsReplicaTransactionAborted(xid);
+
 	/*
 	 * Don't bother checking a transaction older than RecentXmin; it could not
 	 * possibly still be running.  (Note: in particular, this guarantees that
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 07f3c93..204c4cb 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -33,6 +33,7 @@
 #include "postmaster/bgwriter.h"
 #include "storage/fd.h"
 #include "storage/bufmgr.h"
+#include "storage/ipc.h"
 #include "storage/md.h"
 #include "storage/relfilenode.h"
 #include "storage/smgr.h"
@@ -87,6 +88,18 @@ typedef struct _MdfdVec
 
 static MemoryContext MdCxt;		/* context for all MdfdVec objects */
 
+/*
+ * Structure used to collect information created by this backend.
+ * Data of this related should be deleted on backend exit.
+ */
+typedef struct SessionRelation
+{
+	RelFileNodeBackend rnode;
+	struct SessionRelation* next;
+} SessionRelation;
+
+
+static SessionRelation* SessionRelations;
 
 /* Populate a file tag describing an md.c segment file. */
 #define INIT_MD_FILETAG(a,xx_rnode,xx_forknum,xx_segno) \
@@ -152,6 +165,48 @@ mdinit(void)
 								  ALLOCSET_DEFAULT_SIZES);
 }
 
+
+/*
+ * Delete all data of session relations and remove their pages from shared buffers.
+ * This function is called on backend exit.
+ */
+static void
+TruncateSessionRelations(int code, Datum arg)
+{
+	SessionRelation* rel;
+	for (rel = SessionRelations; rel != NULL; rel = rel->next)
+	{
+		/* Remove relation pages from shared buffers */
+		DropRelFileNodesAllBuffers(&rel->rnode, 1);
+
+		/* Delete relation files */
+		mdunlink(rel->rnode, InvalidForkNumber, false);
+	}
+}
+
+/*
+ * Maintain information about session relations accessed by this backend.
+ * This list is needed to perform cleanup on backend exit.
+ * Session relation is linked in this list when this relation is created or opened and file doesn't exist.
+ * Such procedure guarantee that each relation is linked into list only once.
+ */
+static void
+RegisterSessionRelation(SMgrRelation reln)
+{
+	SessionRelation* rel = (SessionRelation*)MemoryContextAlloc(TopMemoryContext, sizeof(SessionRelation));
+
+	/*
+	 * Perform session relation cleanup on backend exit. We are using shared memory hook, because
+	 * cleanup should be performed before backend is disconnected from shared memory.
+	 */
+	if (SessionRelations == NULL)
+		on_shmem_exit(TruncateSessionRelations, 0);
+
+	rel->rnode = reln->smgr_rnode;
+	rel->next = SessionRelations;
+	SessionRelations = rel;
+}
+
 /*
  *	mdexists() -- Does the physical file exist?
  *
@@ -218,6 +273,8 @@ mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo)
 					 errmsg("could not create file \"%s\": %m", path)));
 		}
 	}
+	if (RelFileNodeBackendIsGlobalTemp(reln->smgr_rnode))
+		RegisterSessionRelation(reln);
 
 	pfree(path);
 
@@ -465,6 +522,19 @@ mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
 
 	if (fd < 0)
 	{
+		/*
+		 * In case of session relation access, there may be no yet files of this relation for this backend.
+		 * If so, then create file and register session relation for truncation on backend exit.
+		 */
+		if (RelFileNodeBackendIsGlobalTemp(reln->smgr_rnode))
+		{
+			fd = PathNameOpenFile(path, O_RDWR | PG_BINARY | O_CREAT);
+			if (fd >= 0)
+			{
+				RegisterSessionRelation(reln);
+				goto NewSegment;
+			}
+		}
 		if ((behavior & EXTENSION_RETURN_NULL) &&
 			FILE_POSSIBLY_DELETED(errno))
 		{
@@ -476,6 +546,7 @@ mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
 				 errmsg("could not open file \"%s\": %m", path)));
 	}
 
+  NewSegment:
 	pfree(path);
 
 	_fdvec_resize(reln, forknum, 1);
@@ -652,8 +723,13 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 		 * complaining.  This allows, for example, the case of trying to
 		 * update a block that was later truncated away.
 		 */
-		if (zero_damaged_pages || InRecovery)
+		if (zero_damaged_pages || InRecovery || RelFileNodeBackendIsGlobalTemp(reln->smgr_rnode))
+		{
 			MemSet(buffer, 0, BLCKSZ);
+			/* In case of session relation we need to write zero page to provide correct result of subsequent mdnblocks */
+			if (RelFileNodeBackendIsGlobalTemp(reln->smgr_rnode))
+				mdwrite(reln, forknum, blocknum, buffer, true);
+		}
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
@@ -738,12 +814,18 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 BlockNumber
 mdnblocks(SMgrRelation reln, ForkNumber forknum)
 {
-	MdfdVec    *v = mdopenfork(reln, forknum, EXTENSION_FAIL);
+	/*
+	 * If we access session relation, there may be no files yet of this relation for this backend.
+	 * Pass EXTENSION_RETURN_NULL to make mdopen return NULL in this case instead of reporting error.
+	 */
+	MdfdVec    *v = mdopenfork(reln, forknum, RelFileNodeBackendIsGlobalTemp(reln->smgr_rnode)
+							   ? EXTENSION_RETURN_NULL : EXTENSION_FAIL);
 	BlockNumber nblocks;
 	BlockNumber segno = 0;
 
 	/* mdopen has opened the first segment */
-	Assert(reln->md_num_open_segs[forknum] > 0);
+	if (reln->md_num_open_segs[forknum] == 0)
+		return 0;
 
 	/*
 	 * Start from the last open segments, to avoid redundant seeks.  We have
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index a87e721..2401361 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -994,6 +994,9 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 	/* Determine owning backend. */
 	switch (relform->relpersistence)
 	{
+		case RELPERSISTENCE_SESSION:
+			backend = BackendIdForSessionRelations();
+			break;
 		case RELPERSISTENCE_UNLOGGED:
 		case RELPERSISTENCE_PERMANENT:
 			backend = InvalidBackendId;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 2488607..86e8fca 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1098,6 +1098,10 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	relation->rd_newRelfilenodeSubid = InvalidSubTransactionId;
 	switch (relation->rd_rel->relpersistence)
 	{
+		case RELPERSISTENCE_SESSION:
+			relation->rd_backend = BackendIdForSessionRelations();
+			relation->rd_islocaltemp = false;
+			break;
 		case RELPERSISTENCE_UNLOGGED:
 		case RELPERSISTENCE_PERMANENT:
 			relation->rd_backend = InvalidBackendId;
@@ -3301,6 +3305,10 @@ RelationBuildLocalRelation(const char *relname,
 	rel->rd_rel->relpersistence = relpersistence;
 	switch (relpersistence)
 	{
+		case RELPERSISTENCE_SESSION:
+			rel->rd_backend = BackendIdForSessionRelations();
+			rel->rd_islocaltemp = false;
+			break;
 		case RELPERSISTENCE_UNLOGGED:
 		case RELPERSISTENCE_PERMANENT:
 			rel->rd_backend = InvalidBackendId;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 0cc9ede..1dff0c8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15593,8 +15593,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
 											 tbinfo->dobj.catId.oid, false);
 
 		appendPQExpBuffer(q, "CREATE %s%s %s",
-						  tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
-						  "UNLOGGED " : "",
+						  tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ? "UNLOGGED "
+						  : tbinfo->relpersistence == RELPERSISTENCE_SESSION ? "SESSION " : "",
 						  reltypename,
 						  qualrelname);
 
diff --git a/src/common/relpath.c b/src/common/relpath.c
index 62b9553..cef99d2 100644
--- a/src/common/relpath.c
+++ b/src/common/relpath.c
@@ -166,7 +166,18 @@ GetRelationPath(Oid dbNode, Oid spcNode, Oid relNode,
 		}
 		else
 		{
-			if (forkNumber != MAIN_FORKNUM)
+			/*
+			 * Session relations are distinguished from local temp relations by adding
+			 * SessionRelFirstBackendId offset to backendId.
+			 * These is no need to separate them at file system level, so just subtract SessionRelFirstBackendId
+			 * to avoid too long file names.
+			 * Segments of session relations have the same prefix (t%d_) as local temporary relations
+			 * to make it possible to cleanup them in the same way as local temporary relation files.
+			 */
+			 if (backendId >= SessionRelFirstBackendId)
+				 backendId -= SessionRelFirstBackendId;
+
+			 if (forkNumber != MAIN_FORKNUM)
 				path = psprintf("base/%u/t%d_%u_%s",
 								dbNode, backendId, relNode,
 								forkNames[forkNumber]);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 858bcb6..2f16c58 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -195,9 +195,9 @@ extern void heap_vacuum_rel(Relation onerel,
 							struct VacuumParams *params, BufferAccessStrategy bstrategy);
 
 /* in heap/heapam_visibility.c */
-extern bool HeapTupleSatisfiesVisibility(HeapTuple stup, Snapshot snapshot,
+extern bool HeapTupleSatisfiesVisibility(Relation relation, HeapTuple stup, Snapshot snapshot,
 										 Buffer buffer);
-extern TM_Result HeapTupleSatisfiesUpdate(HeapTuple stup, CommandId curcid,
+extern TM_Result HeapTupleSatisfiesUpdate(Relation relation, HeapTuple stup, CommandId curcid,
 										  Buffer buffer);
 extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple stup, TransactionId OldestXmin,
 											Buffer buffer);
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index d714551..cbe6760 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -41,6 +41,9 @@
 extern int	DefaultXactIsoLevel;
 extern PGDLLIMPORT int XactIsoLevel;
 
+extern bool AccessTempRelationAtReplica;
+
+
 /*
  * We implement three isolation levels internally.
  * The two stronger ones use one snapshot per database transaction;
@@ -440,4 +443,8 @@ extern void EnterParallelMode(void);
 extern void ExitParallelMode(void);
 extern bool IsInParallelMode(void);
 
+extern TransactionId GetReplicaTransactionId(void);
+extern bool IsReplicaTransactionAborted(TransactionId xid);
+extern bool IsReplicaCurrentTransactionId(TransactionId xid);
+
 #endif							/* XACT_H */
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 090b6ba..6a39663 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -165,6 +165,7 @@ typedef FormData_pg_class *Form_pg_class;
 #define		  RELPERSISTENCE_PERMANENT	'p' /* regular table */
 #define		  RELPERSISTENCE_UNLOGGED	'u' /* unlogged permanent table */
 #define		  RELPERSISTENCE_TEMP		't' /* temporary table */
+#define		  RELPERSISTENCE_SESSION	's' /* session table */
 
 /* default selection for replica identity (primary key or nothing) */
 #define		  REPLICA_IDENTITY_DEFAULT	'd'
diff --git a/src/include/storage/backendid.h b/src/include/storage/backendid.h
index 70ef8eb..f226e7c 100644
--- a/src/include/storage/backendid.h
+++ b/src/include/storage/backendid.h
@@ -22,6 +22,13 @@ typedef int BackendId;			/* unique currently active backend identifier */
 
 #define InvalidBackendId		(-1)
 
+/*
+ * We need to distinguish local and global temporary relations by RelFileNodeBackend.
+ * The least invasive change is to add some special bias value to backend id (since 
+ * maximal number of backed is limited by MaxBackends).
+ */
+#define SessionRelFirstBackendId (0x40000000)
+
 extern PGDLLIMPORT BackendId MyBackendId;	/* backend id of this backend */
 
 /* backend id of our parallel session leader, or InvalidBackendId if none */
@@ -34,4 +41,10 @@ extern PGDLLIMPORT BackendId ParallelMasterBackendId;
 #define BackendIdForTempRelations() \
 	(ParallelMasterBackendId == InvalidBackendId ? MyBackendId : ParallelMasterBackendId)
 
+
+#define BackendIdForSessionRelations() \
+	(BackendIdForTempRelations() + SessionRelFirstBackendId)
+
+#define IsSessionRelationBackendId(id) ((id) >= SessionRelFirstBackendId)
+
 #endif							/* BACKENDID_H */
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index df2dda7..7adb96b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -90,16 +90,17 @@
  */
 typedef struct buftag
 {
-	RelFileNode rnode;			/* physical relation identifier */
+	RelFileNodeBackend rnode;			/* physical relation identifier */
 	ForkNumber	forkNum;
 	BlockNumber blockNum;		/* blknum relative to begin of reln */
 } BufferTag;
 
 #define CLEAR_BUFFERTAG(a) \
 ( \
-	(a).rnode.spcNode = InvalidOid, \
-	(a).rnode.dbNode = InvalidOid, \
-	(a).rnode.relNode = InvalidOid, \
+	(a).rnode.node.spcNode = InvalidOid, \
+	(a).rnode.node.dbNode = InvalidOid, \
+	(a).rnode.node.relNode = InvalidOid, \
+	(a).rnode.backend = InvalidBackendId, \
 	(a).forkNum = InvalidForkNumber, \
 	(a).blockNum = InvalidBlockNumber \
 )
@@ -113,7 +114,7 @@ typedef struct buftag
 
 #define BUFFERTAGS_EQUAL(a,b) \
 ( \
-	RelFileNodeEquals((a).rnode, (b).rnode) && \
+	RelFileNodeBackendEquals((a).rnode, (b).rnode) && \
 	(a).blockNum == (b).blockNum && \
 	(a).forkNum == (b).forkNum \
 )
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 509f4b7..3315fa0 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -205,7 +205,7 @@ extern XLogRecPtr BufferGetLSNAtomic(Buffer buffer);
 extern void PrintPinnedBufs(void);
 #endif
 extern Size BufferShmemSize(void);
-extern void BufferGetTag(Buffer buffer, RelFileNode *rnode,
+extern void BufferGetTag(Buffer buffer, RelFileNodeBackend *rnode,
 						 ForkNumber *forknum, BlockNumber *blknum);
 
 extern void MarkBufferDirtyHint(Buffer buffer, bool buffer_std);
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 4ef6d8d..bac7a31 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -229,6 +229,13 @@ typedef PageHeaderData *PageHeader;
 #define PageIsNew(page) (((PageHeader) (page))->pd_upper == 0)
 
 /*
+ * Page of temporary relation is not initialized
+ */
+#define GlobalTempRelationPageIsNotInitialized(rel, page) \
+	((rel)->rd_rel->relpersistence == RELPERSISTENCE_SESSION && PageIsNew(page))
+
+
+/*
  * PageGetItemId
  *		Returns an item identifier of a page.
  */
diff --git a/src/include/storage/relfilenode.h b/src/include/storage/relfilenode.h
index 586500a..20aec72 100644
--- a/src/include/storage/relfilenode.h
+++ b/src/include/storage/relfilenode.h
@@ -75,10 +75,25 @@ typedef struct RelFileNodeBackend
 	BackendId	backend;
 } RelFileNodeBackend;
 
+/*
+ * Check whether it is local or global temporary relation, which data belongs only to one backend.
+ */
 #define RelFileNodeBackendIsTemp(rnode) \
 	((rnode).backend != InvalidBackendId)
 
 /*
+ * Check whether it is global temporary relation which metadata is shared by all sessions,
+ * but data is private for the current session.
+ */
+#define RelFileNodeBackendIsGlobalTemp(rnode) IsSessionRelationBackendId((rnode).backend)
+
+/*
+ * Check whether it is local temporary relation which exists only in this backend.
+ */
+#define RelFileNodeBackendIsLocalTemp(rnode) \
+	(RelFileNodeBackendIsTemp(rnode) && !RelFileNodeBackendIsGlobalTemp(rnode))
+
+/*
  * Note: RelFileNodeEquals and RelFileNodeBackendEquals compare relNode first
  * since that is most likely to be different in two unequal RelFileNodes.  It
  * is probably redundant to compare spcNode if the other fields are found equal,
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index b0fe19e..b361851 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -328,6 +328,17 @@ typedef struct StdRdOptions
 	((relation)->rd_options ? \
 	 ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
 
+/*
+ * Relation persistence is either TEMP either SESSION
+ */
+#define IsLocalRelpersistence(relpersistence) \
+	((relpersistence) == RELPERSISTENCE_TEMP || (relpersistence) == RELPERSISTENCE_SESSION)
+
+/*
+ * Relation is either global either local temp table
+ */
+#define RelationHasSessionScope(relation) \
+	IsLocalRelpersistence(((relation)->rd_rel->relpersistence))
 
 /*
  * ViewOptions
diff --git a/src/test/isolation/expected/inherit-global-temp.out b/src/test/isolation/expected/inherit-global-temp.out
new file mode 100644
index 0000000..6114f8c
--- /dev/null
+++ b/src/test/isolation/expected/inherit-global-temp.out
@@ -0,0 +1,218 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_insert_p s1_insert_c s2_insert_c s1_select_p s1_select_c s2_select_p s2_select_c
+step s1_insert_p: INSERT INTO inh_global_parent VALUES (1), (2);
+step s1_insert_c: INSERT INTO inh_global_temp_child_s1 VALUES (3), (4);
+step s2_insert_c: INSERT INTO inh_global_temp_child_s2 VALUES (5), (6);
+step s1_select_p: SELECT a FROM inh_global_parent;
+a              
+
+1              
+2              
+3              
+4              
+step s1_select_c: SELECT a FROM inh_global_temp_child_s1;
+a              
+
+3              
+4              
+step s2_select_p: SELECT a FROM inh_global_parent;
+a              
+
+1              
+2              
+5              
+6              
+step s2_select_c: SELECT a FROM inh_global_temp_child_s2;
+a              
+
+5              
+6              
+
+starting permutation: s1_insert_p s1_insert_c s2_insert_c s1_update_p s1_update_c s1_select_p s1_select_c s2_select_p s2_select_c
+step s1_insert_p: INSERT INTO inh_global_parent VALUES (1), (2);
+step s1_insert_c: INSERT INTO inh_global_temp_child_s1 VALUES (3), (4);
+step s2_insert_c: INSERT INTO inh_global_temp_child_s2 VALUES (5), (6);
+step s1_update_p: UPDATE inh_global_parent SET a = 11 WHERE a = 1;
+step s1_update_c: UPDATE inh_global_parent SET a = 13 WHERE a IN (3, 5);
+step s1_select_p: SELECT a FROM inh_global_parent;
+a              
+
+2              
+11             
+4              
+13             
+step s1_select_c: SELECT a FROM inh_global_temp_child_s1;
+a              
+
+4              
+13             
+step s2_select_p: SELECT a FROM inh_global_parent;
+a              
+
+2              
+11             
+5              
+6              
+step s2_select_c: SELECT a FROM inh_global_temp_child_s2;
+a              
+
+5              
+6              
+
+starting permutation: s1_insert_p s1_insert_c s2_insert_c s2_update_c s1_select_p s1_select_c s2_select_p s2_select_c
+step s1_insert_p: INSERT INTO inh_global_parent VALUES (1), (2);
+step s1_insert_c: INSERT INTO inh_global_temp_child_s1 VALUES (3), (4);
+step s2_insert_c: INSERT INTO inh_global_temp_child_s2 VALUES (5), (6);
+step s2_update_c: UPDATE inh_global_parent SET a = 15 WHERE a IN (3, 5);
+step s1_select_p: SELECT a FROM inh_global_parent;
+a              
+
+1              
+2              
+3              
+4              
+step s1_select_c: SELECT a FROM inh_global_temp_child_s1;
+a              
+
+3              
+4              
+step s2_select_p: SELECT a FROM inh_global_parent;
+a              
+
+1              
+2              
+6              
+15             
+step s2_select_c: SELECT a FROM inh_global_temp_child_s2;
+a              
+
+6              
+15             
+
+starting permutation: s1_insert_p s1_insert_c s2_insert_c s1_delete_p s1_delete_c s1_select_p s1_select_c s2_select_p s2_select_c
+step s1_insert_p: INSERT INTO inh_global_parent VALUES (1), (2);
+step s1_insert_c: INSERT INTO inh_global_temp_child_s1 VALUES (3), (4);
+step s2_insert_c: INSERT INTO inh_global_temp_child_s2 VALUES (5), (6);
+step s1_delete_p: DELETE FROM inh_global_parent WHERE a = 2;
+step s1_delete_c: DELETE FROM inh_global_parent WHERE a IN (4, 6);
+step s1_select_p: SELECT a FROM inh_global_parent;
+a              
+
+1              
+3              
+step s1_select_c: SELECT a FROM inh_global_temp_child_s1;
+a              
+
+3              
+step s2_select_p: SELECT a FROM inh_global_parent;
+a              
+
+1              
+5              
+6              
+step s2_select_c: SELECT a FROM inh_global_temp_child_s2;
+a              
+
+5              
+6              
+
+starting permutation: s1_insert_p s1_insert_c s2_insert_c s2_delete_c s1_select_p s1_select_c s2_select_p s2_select_c
+step s1_insert_p: INSERT INTO inh_global_parent VALUES (1), (2);
+step s1_insert_c: INSERT INTO inh_global_temp_child_s1 VALUES (3), (4);
+step s2_insert_c: INSERT INTO inh_global_temp_child_s2 VALUES (5), (6);
+step s2_delete_c: DELETE FROM inh_global_parent WHERE a IN (4, 6);
+step s1_select_p: SELECT a FROM inh_global_parent;
+a              
+
+1              
+2              
+3              
+4              
+step s1_select_c: SELECT a FROM inh_global_temp_child_s1;
+a              
+
+3              
+4              
+step s2_select_p: SELECT a FROM inh_global_parent;
+a              
+
+1              
+2              
+5              
+step s2_select_c: SELECT a FROM inh_global_temp_child_s2;
+a              
+
+5              
+
+starting permutation: s1_insert_p s1_insert_c s2_insert_c s1_truncate_p s1_select_p s1_select_c s2_select_p s2_select_c
+step s1_insert_p: INSERT INTO inh_global_parent VALUES (1), (2);
+step s1_insert_c: INSERT INTO inh_global_temp_child_s1 VALUES (3), (4);
+step s2_insert_c: INSERT INTO inh_global_temp_child_s2 VALUES (5), (6);
+step s1_truncate_p: TRUNCATE inh_global_parent;
+step s1_select_p: SELECT a FROM inh_global_parent;
+a              
+
+step s1_select_c: SELECT a FROM inh_global_temp_child_s1;
+a              
+
+step s2_select_p: SELECT a FROM inh_global_parent;
+a              
+
+5              
+6              
+step s2_select_c: SELECT a FROM inh_global_temp_child_s2;
+a              
+
+5              
+6              
+
+starting permutation: s1_insert_p s1_insert_c s2_insert_c s2_truncate_p s1_select_p s1_select_c s2_select_p s2_select_c
+step s1_insert_p: INSERT INTO inh_global_parent VALUES (1), (2);
+step s1_insert_c: INSERT INTO inh_global_temp_child_s1 VALUES (3), (4);
+step s2_insert_c: INSERT INTO inh_global_temp_child_s2 VALUES (5), (6);
+step s2_truncate_p: TRUNCATE inh_global_parent;
+step s1_select_p: SELECT a FROM inh_global_parent;
+a              
+
+3              
+4              
+step s1_select_c: SELECT a FROM inh_global_temp_child_s1;
+a              
+
+3              
+4              
+step s2_select_p: SELECT a FROM inh_global_parent;
+a              
+
+step s2_select_c: SELECT a FROM inh_global_temp_child_s2;
+a              
+
+
+starting permutation: s1_insert_p s1_insert_c s2_insert_c s1_begin s1_truncate_p s2_select_p s1_commit
+step s1_insert_p: INSERT INTO inh_global_parent VALUES (1), (2);
+step s1_insert_c: INSERT INTO inh_global_temp_child_s1 VALUES (3), (4);
+step s2_insert_c: INSERT INTO inh_global_temp_child_s2 VALUES (5), (6);
+step s1_begin: BEGIN;
+step s1_truncate_p: TRUNCATE inh_global_parent;
+step s2_select_p: SELECT a FROM inh_global_parent; <waiting ...>
+step s1_commit: COMMIT;
+step s2_select_p: <... completed>
+a              
+
+5              
+6              
+
+starting permutation: s1_insert_p s1_insert_c s2_insert_c s1_begin s1_truncate_p s2_select_c s1_commit
+step s1_insert_p: INSERT INTO inh_global_parent VALUES (1), (2);
+step s1_insert_c: INSERT INTO inh_global_temp_child_s1 VALUES (3), (4);
+step s2_insert_c: INSERT INTO inh_global_temp_child_s2 VALUES (5), (6);
+step s1_begin: BEGIN;
+step s1_truncate_p: TRUNCATE inh_global_parent;
+step s2_select_c: SELECT a FROM inh_global_temp_child_s2; <waiting ...>
+step s1_commit: COMMIT;
+step s2_select_c: <... completed>
+a              
+
+5              
+6              
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 74b5077..44df4e0 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -85,3 +85,4 @@ test: plpgsql-toast
 test: truncate-conflict
 test: serializable-parallel
 test: serializable-parallel-2
+test: inherit-global-temp
diff --git a/src/test/isolation/specs/inherit-global-temp.spec b/src/test/isolation/specs/inherit-global-temp.spec
new file mode 100644
index 0000000..5e95dd6
--- /dev/null
+++ b/src/test/isolation/specs/inherit-global-temp.spec
@@ -0,0 +1,73 @@
+# This is a copy of the inherit-temp test with little changes for global temporary tables.
+#
+
+setup
+{
+  CREATE TABLE inh_global_parent (a int);
+}
+
+teardown
+{
+  DROP TABLE inh_global_parent;
+}
+
+# Session 1 executes actions which act directly on both the parent and
+# its child.  Abbreviation "c" is used for queries working on the child
+# and "p" on the parent.
+session "s1"
+setup
+{
+  CREATE GLOBAL TEMPORARY TABLE inh_global_temp_child_s1 () INHERITS (inh_global_parent);
+}
+step "s1_begin" { BEGIN; }
+step "s1_truncate_p" { TRUNCATE inh_global_parent; }
+step "s1_select_p" { SELECT a FROM inh_global_parent; }
+step "s1_select_c" { SELECT a FROM inh_global_temp_child_s1; }
+step "s1_insert_p" { INSERT INTO inh_global_parent VALUES (1), (2); }
+step "s1_insert_c" { INSERT INTO inh_global_temp_child_s1 VALUES (3), (4); }
+step "s1_update_p" { UPDATE inh_global_parent SET a = 11 WHERE a = 1; }
+step "s1_update_c" { UPDATE inh_global_parent SET a = 13 WHERE a IN (3, 5); }
+step "s1_delete_p" { DELETE FROM inh_global_parent WHERE a = 2; }
+step "s1_delete_c" { DELETE FROM inh_global_parent WHERE a IN (4, 6); }
+step "s1_commit" { COMMIT; }
+teardown
+{
+  DROP TABLE inh_global_temp_child_s1;
+}
+
+# Session 2 executes actions on the parent which act only on the child.
+session "s2"
+setup
+{
+  CREATE GLOBAL TEMPORARY TABLE inh_global_temp_child_s2 () INHERITS (inh_global_parent);
+}
+step "s2_truncate_p" { TRUNCATE inh_global_parent; }
+step "s2_select_p" { SELECT a FROM inh_global_parent; }
+step "s2_select_c" { SELECT a FROM inh_global_temp_child_s2; }
+step "s2_insert_c" { INSERT INTO inh_global_temp_child_s2 VALUES (5), (6); }
+step "s2_update_c" { UPDATE inh_global_parent SET a = 15 WHERE a IN (3, 5); }
+step "s2_delete_c" { DELETE FROM inh_global_parent WHERE a IN (4, 6); }
+teardown
+{
+  DROP TABLE inh_global_temp_child_s2;
+}
+
+# Check INSERT behavior across sessions
+permutation "s1_insert_p" "s1_insert_c" "s2_insert_c" "s1_select_p" "s1_select_c" "s2_select_p" "s2_select_c"
+
+# Check UPDATE behavior across sessions
+permutation "s1_insert_p" "s1_insert_c" "s2_insert_c" "s1_update_p" "s1_update_c" "s1_select_p" "s1_select_c" "s2_select_p" "s2_select_c"
+permutation "s1_insert_p" "s1_insert_c" "s2_insert_c" "s2_update_c" "s1_select_p" "s1_select_c" "s2_select_p" "s2_select_c"
+
+# Check DELETE behavior across sessions
+permutation "s1_insert_p" "s1_insert_c" "s2_insert_c" "s1_delete_p" "s1_delete_c" "s1_select_p" "s1_select_c" "s2_select_p" "s2_select_c"
+permutation "s1_insert_p" "s1_insert_c" "s2_insert_c" "s2_delete_c" "s1_select_p" "s1_select_c" "s2_select_p" "s2_select_c"
+
+# Check TRUNCATE behavior across sessions
+permutation "s1_insert_p" "s1_insert_c" "s2_insert_c" "s1_truncate_p" "s1_select_p" "s1_select_c" "s2_select_p" "s2_select_c"
+permutation "s1_insert_p" "s1_insert_c" "s2_insert_c" "s2_truncate_p" "s1_select_p" "s1_select_c" "s2_select_p" "s2_select_c"
+
+# TRUNCATE on a parent tree does not block access to temporary child relation
+# of another session, and blocks when scanning the parent.
+permutation "s1_insert_p" "s1_insert_c" "s2_insert_c" "s1_begin" "s1_truncate_p" "s2_select_p" "s1_commit"
+permutation "s1_insert_p" "s1_insert_c" "s2_insert_c" "s1_begin" "s1_truncate_p" "s2_select_c" "s1_commit"
diff --git a/src/test/regress/expected/global_temp.out b/src/test/regress/expected/global_temp.out
new file mode 100644
index 0000000..b7bf067
--- /dev/null
+++ b/src/test/regress/expected/global_temp.out
@@ -0,0 +1,323 @@
+--
+-- GLOBAL TEMP
+-- Test global temp relations
+--
+-- Test ON COMMIT DELETE ROWS
+CREATE GLOBAL TEMP TABLE global_temptest(col int) ON COMMIT DELETE ROWS;
+BEGIN;
+INSERT INTO global_temptest VALUES (1);
+INSERT INTO global_temptest VALUES (2);
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+   2
+(2 rows)
+
+COMMIT;
+SELECT * FROM global_temptest;
+ col 
+-----
+(0 rows)
+
+DROP TABLE global_temptest;
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temptest(col) ON COMMIT DELETE ROWS AS SELECT 1;
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+(1 row)
+
+COMMIT;
+SELECT * FROM global_temptest;
+ col 
+-----
+(0 rows)
+
+DROP TABLE global_temptest;
+-- Test foreign keys
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temptest1(col int PRIMARY KEY);
+CREATE GLOBAL TEMP TABLE global_temptest2(col int REFERENCES global_temptest1)
+  ON COMMIT DELETE ROWS;
+INSERT INTO global_temptest1 VALUES (1);
+INSERT INTO global_temptest2 VALUES (1);
+COMMIT;
+SELECT * FROM global_temptest1;
+ col 
+-----
+   1
+(1 row)
+
+SELECT * FROM global_temptest2;
+ col 
+-----
+(0 rows)
+
+DROP TABLE global_temptest2;
+DROP TABLE global_temptest1;
+-- Unsupported ON COMMIT and foreign key combination
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temptest4(col int REFERENCES global_temptest3);
+COMMIT;
+ERROR:  unsupported ON COMMIT and foreign key combination
+DETAIL:  Table "global_temptest4" references "global_temptest3", but they do not have the same ON COMMIT setting.
+-- For partitioned temp tables, ON COMMIT actions ignore storage-less
+-- partitioned tables.
+BEGIN;
+CREATE GLOBAL TEMP TABLE temp_parted_oncommit (a int)
+  PARTITION BY LIST (a) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE temp_parted_oncommit_1
+  PARTITION OF temp_parted_oncommit
+  FOR VALUES IN (1) ON COMMIT DELETE ROWS;
+INSERT INTO temp_parted_oncommit VALUES (1);
+COMMIT;
+-- partitions are emptied by the previous commit
+SELECT * FROM temp_parted_oncommit;
+ a 
+---
+(0 rows)
+
+DROP TABLE temp_parted_oncommit;
+-- Using ON COMMIT DELETE on a partitioned table does not remove
+-- all rows if partitions preserve their data.
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temp_parted_oncommit_test (a int)
+  PARTITION BY LIST (a) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temp_parted_oncommit_test1
+  PARTITION OF global_temp_parted_oncommit_test
+  FOR VALUES IN (1) ON COMMIT PRESERVE ROWS;
+INSERT INTO global_temp_parted_oncommit_test VALUES (1);
+COMMIT;
+-- Data from the remaining partition is still here as its rows are
+-- preserved.
+SELECT * FROM global_temp_parted_oncommit_test;
+ a 
+---
+ 1
+(1 row)
+
+-- two relations remain in this case.
+SELECT relname FROM pg_class WHERE relname LIKE 'global_temp_parted_oncommit_test%';
+              relname              
+-----------------------------------
+ global_temp_parted_oncommit_test
+ global_temp_parted_oncommit_test1
+(2 rows)
+
+DROP TABLE global_temp_parted_oncommit_test;
+-- Check dependencies between ON COMMIT actions with inheritance trees.
+-- Data on the parent is removed, and the child goes away.
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temp_inh_oncommit_test (a int) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temp_inh_oncommit_test1 ()
+  INHERITS(global_temp_inh_oncommit_test) ON COMMIT PRESERVE ROWS;
+INSERT INTO global_temp_inh_oncommit_test1 VALUES (1);
+INSERT INTO global_temp_inh_oncommit_test VALUES (1);
+COMMIT;
+SELECT * FROM global_temp_inh_oncommit_test;
+ a 
+---
+ 1
+(1 row)
+
+-- two relations remain
+SELECT relname FROM pg_class WHERE relname LIKE 'global_temp_inh_oncommit_test%';
+            relname             
+--------------------------------
+ global_temp_inh_oncommit_test
+ global_temp_inh_oncommit_test1
+(2 rows)
+
+DROP TABLE global_temp_inh_oncommit_test1;
+DROP TABLE global_temp_inh_oncommit_test;
+-- Global temp table cannot inherit from temporary relation
+BEGIN;
+CREATE TEMP TABLE global_temp_table (a int) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temp_table1 ()
+  INHERITS(global_temp_table) ON COMMIT PRESERVE ROWS;
+ERROR:  cannot inherit from temporary relation "global_temp_table"
+ROLLBACK;
+-- Temp table can inherit from global temporary relation
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temp_table (a int) ON COMMIT DELETE ROWS;
+CREATE TEMP TABLE temp_table1 ()
+  INHERITS(global_temp_table) ON COMMIT PRESERVE ROWS;
+CREATE TEMP TABLE temp_table2 ()
+  INHERITS(global_temp_table) ON COMMIT DELETE ROWS;
+INSERT INTO temp_table2 VALUES (2);
+INSERT INTO temp_table1 VALUES (1);
+INSERT INTO global_temp_table VALUES (0);
+SELECT * FROM global_temp_table;
+ a 
+---
+ 0
+ 1
+ 2
+(3 rows)
+
+COMMIT;
+SELECT * FROM global_temp_table;
+ a 
+---
+ 1
+(1 row)
+
+DROP TABLE temp_table2;
+DROP TABLE temp_table1;
+DROP TABLE global_temp_table;
+-- Global temp table can inherit from normal relation
+BEGIN;
+CREATE TABLE normal_table (a int);
+CREATE GLOBAL TEMP TABLE temp_table1 ()
+  INHERITS(normal_table) ON COMMIT PRESERVE ROWS;
+CREATE GLOBAL TEMP TABLE temp_table2 ()
+  INHERITS(normal_table) ON COMMIT DELETE ROWS;
+INSERT INTO temp_table2 VALUES (2);
+INSERT INTO temp_table1 VALUES (1);
+INSERT INTO normal_table VALUES (0);
+SELECT * FROM normal_table;
+ a 
+---
+ 0
+ 1
+ 2
+(3 rows)
+
+COMMIT;
+SELECT * FROM normal_table;
+ a 
+---
+ 0
+ 1
+(2 rows)
+
+DROP TABLE temp_table2;
+DROP TABLE temp_table1;
+DROP TABLE normal_table;
+-- Check SERIAL and BIGSERIAL pseudo-types
+CREATE GLOBAL TEMP TABLE global_temp_table ( aid BIGSERIAL, bid SERIAL );
+CREATE SEQUENCE test_sequence;
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+SELECT * FROM global_temp_table;
+ aid | bid 
+-----+-----
+   1 |   1
+   2 |   2
+   3 |   3
+(3 rows)
+
+SELECT NEXTVAL( 'test_sequence' );
+ nextval 
+---------
+       1
+(1 row)
+
+\c
+SELECT * FROM global_temp_table;
+ aid | bid 
+-----+-----
+(0 rows)
+
+SELECT NEXTVAL( 'test_sequence' );
+ nextval 
+---------
+       2
+(1 row)
+
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+SELECT * FROM global_temp_table;
+ aid | bid 
+-----+-----
+   1 |   1
+   2 |   2
+   3 |   3
+(3 rows)
+
+SELECT NEXTVAL( 'test_sequence' );
+ nextval 
+---------
+       3
+(1 row)
+
+DROP TABLE global_temp_table;
+DROP SEQUENCE test_sequence;
+-- Test two phase commit
+CREATE TABLE global_temptest_persistent(col int);
+CREATE GLOBAL TEMP TABLE global_temptest(col int);
+INSERT INTO global_temptest VALUES (1);
+BEGIN;
+INSERT INTO global_temptest VALUES (2);
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+   2
+(2 rows)
+
+PREPARE TRANSACTION 'global_temp1';
+-- We can't see anything from an uncommitted transaction
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+(1 row)
+
+BEGIN;
+INSERT INTO global_temptest VALUES (3);
+INSERT INTO global_temptest_persistent SELECT * FROM global_temptest;
+PREPARE TRANSACTION 'global_temp2';
+COMMIT PREPARED 'global_temp1';
+-- 1, 2
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+   2
+(2 rows)
+
+-- Nothing
+SELECT * FROM global_temptest_persistent;
+ col 
+-----
+(0 rows)
+
+\c
+-- The temp table is empty now.
+SELECT * FROM global_temptest;
+ col 
+-----
+(0 rows)
+
+-- Still nothing in global_temptest_persistent table;
+SELECT * FROM global_temptest_persistent;
+ col 
+-----
+(0 rows)
+
+INSERT INTO global_temptest VALUES (4);
+COMMIT PREPARED 'global_temp2';
+-- Only 4
+SELECT * FROM global_temptest;
+ col 
+-----
+   4
+(1 row)
+
+-- 1, 3
+SELECT * FROM global_temptest_persistent;
+ col 
+-----
+   1
+   3
+(2 rows)
+
+\c
+DROP TABLE global_temptest;
+DROP TABLE global_temptest_persistent;
diff --git a/src/test/regress/expected/global_temp_0.out b/src/test/regress/expected/global_temp_0.out
new file mode 100644
index 0000000..934e751
--- /dev/null
+++ b/src/test/regress/expected/global_temp_0.out
@@ -0,0 +1,326 @@
+--
+-- GLOBAL TEMP
+-- Test global temp relations
+--
+-- Test ON COMMIT DELETE ROWS
+CREATE GLOBAL TEMP TABLE global_temptest(col int) ON COMMIT DELETE ROWS;
+BEGIN;
+INSERT INTO global_temptest VALUES (1);
+INSERT INTO global_temptest VALUES (2);
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+   2
+(2 rows)
+
+COMMIT;
+SELECT * FROM global_temptest;
+ col 
+-----
+(0 rows)
+
+DROP TABLE global_temptest;
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temptest(col) ON COMMIT DELETE ROWS AS SELECT 1;
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+(1 row)
+
+COMMIT;
+SELECT * FROM global_temptest;
+ col 
+-----
+(0 rows)
+
+DROP TABLE global_temptest;
+-- Test foreign keys
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temptest1(col int PRIMARY KEY);
+CREATE GLOBAL TEMP TABLE global_temptest2(col int REFERENCES global_temptest1)
+  ON COMMIT DELETE ROWS;
+INSERT INTO global_temptest1 VALUES (1);
+INSERT INTO global_temptest2 VALUES (1);
+COMMIT;
+SELECT * FROM global_temptest1;
+ col 
+-----
+   1
+(1 row)
+
+SELECT * FROM global_temptest2;
+ col 
+-----
+(0 rows)
+
+DROP TABLE global_temptest2;
+DROP TABLE global_temptest1;
+-- Unsupported ON COMMIT and foreign key combination
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temptest4(col int REFERENCES global_temptest3);
+COMMIT;
+ERROR:  unsupported ON COMMIT and foreign key combination
+DETAIL:  Table "global_temptest4" references "global_temptest3", but they do not have the same ON COMMIT setting.
+-- For partitioned temp tables, ON COMMIT actions ignore storage-less
+-- partitioned tables.
+BEGIN;
+CREATE GLOBAL TEMP TABLE temp_parted_oncommit (a int)
+  PARTITION BY LIST (a) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE temp_parted_oncommit_1
+  PARTITION OF temp_parted_oncommit
+  FOR VALUES IN (1) ON COMMIT DELETE ROWS;
+INSERT INTO temp_parted_oncommit VALUES (1);
+COMMIT;
+-- partitions are emptied by the previous commit
+SELECT * FROM temp_parted_oncommit;
+ a 
+---
+(0 rows)
+
+DROP TABLE temp_parted_oncommit;
+-- Using ON COMMIT DELETE on a partitioned table does not remove
+-- all rows if partitions preserve their data.
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temp_parted_oncommit_test (a int)
+  PARTITION BY LIST (a) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temp_parted_oncommit_test1
+  PARTITION OF global_temp_parted_oncommit_test
+  FOR VALUES IN (1) ON COMMIT PRESERVE ROWS;
+INSERT INTO global_temp_parted_oncommit_test VALUES (1);
+COMMIT;
+-- Data from the remaining partition is still here as its rows are
+-- preserved.
+SELECT * FROM global_temp_parted_oncommit_test;
+ a 
+---
+ 1
+(1 row)
+
+-- two relations remain in this case.
+SELECT relname FROM pg_class WHERE relname LIKE 'global_temp_parted_oncommit_test%';
+              relname              
+-----------------------------------
+ global_temp_parted_oncommit_test
+ global_temp_parted_oncommit_test1
+(2 rows)
+
+DROP TABLE global_temp_parted_oncommit_test;
+-- Check dependencies between ON COMMIT actions with inheritance trees.
+-- Data on the parent is removed, and the child goes away.
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temp_inh_oncommit_test (a int) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temp_inh_oncommit_test1 ()
+  INHERITS(global_temp_inh_oncommit_test) ON COMMIT PRESERVE ROWS;
+INSERT INTO global_temp_inh_oncommit_test1 VALUES (1);
+INSERT INTO global_temp_inh_oncommit_test VALUES (1);
+COMMIT;
+SELECT * FROM global_temp_inh_oncommit_test;
+ a 
+---
+ 1
+(1 row)
+
+-- two relations remain
+SELECT relname FROM pg_class WHERE relname LIKE 'global_temp_inh_oncommit_test%';
+            relname             
+--------------------------------
+ global_temp_inh_oncommit_test
+ global_temp_inh_oncommit_test1
+(2 rows)
+
+DROP TABLE global_temp_inh_oncommit_test1;
+DROP TABLE global_temp_inh_oncommit_test;
+-- Global temp table cannot inherit from temporary relation
+BEGIN;
+CREATE TEMP TABLE global_temp_table (a int) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temp_table1 ()
+  INHERITS(global_temp_table) ON COMMIT PRESERVE ROWS;
+ERROR:  cannot inherit from temporary relation "global_temp_table"
+ROLLBACK;
+-- Temp table can inherit from global temporary relation
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temp_table (a int) ON COMMIT DELETE ROWS;
+CREATE TEMP TABLE temp_table1 ()
+  INHERITS(global_temp_table) ON COMMIT PRESERVE ROWS;
+CREATE TEMP TABLE temp_table2 ()
+  INHERITS(global_temp_table) ON COMMIT DELETE ROWS;
+INSERT INTO temp_table2 VALUES (2);
+INSERT INTO temp_table1 VALUES (1);
+INSERT INTO global_temp_table VALUES (0);
+SELECT * FROM global_temp_table;
+ a 
+---
+ 0
+ 1
+ 2
+(3 rows)
+
+COMMIT;
+SELECT * FROM global_temp_table;
+ a 
+---
+ 1
+(1 row)
+
+DROP TABLE temp_table2;
+DROP TABLE temp_table1;
+DROP TABLE global_temp_table;
+-- Global temp table can inherit from normal relation
+BEGIN;
+CREATE TABLE normal_table (a int);
+CREATE GLOBAL TEMP TABLE temp_table1 ()
+  INHERITS(normal_table) ON COMMIT PRESERVE ROWS;
+CREATE GLOBAL TEMP TABLE temp_table2 ()
+  INHERITS(normal_table) ON COMMIT DELETE ROWS;
+INSERT INTO temp_table2 VALUES (2);
+INSERT INTO temp_table1 VALUES (1);
+INSERT INTO normal_table VALUES (0);
+SELECT * FROM normal_table;
+ a 
+---
+ 0
+ 1
+ 2
+(3 rows)
+
+COMMIT;
+SELECT * FROM normal_table;
+ a 
+---
+ 0
+ 1
+(2 rows)
+
+DROP TABLE temp_table2;
+DROP TABLE temp_table1;
+DROP TABLE normal_table;
+-- Check SERIAL and BIGSERIAL pseudo-types
+CREATE GLOBAL TEMP TABLE global_temp_table ( aid BIGSERIAL, bid SERIAL );
+CREATE SEQUENCE test_sequence;
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+SELECT * FROM global_temp_table;
+ aid | bid 
+-----+-----
+   1 |   1
+   2 |   2
+   3 |   3
+(3 rows)
+
+SELECT NEXTVAL( 'test_sequence' );
+ nextval 
+---------
+       1
+(1 row)
+
+\c
+SELECT * FROM global_temp_table;
+ aid | bid 
+-----+-----
+(0 rows)
+
+SELECT NEXTVAL( 'test_sequence' );
+ nextval 
+---------
+       2
+(1 row)
+
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+SELECT * FROM global_temp_table;
+ aid | bid 
+-----+-----
+   1 |   1
+   2 |   2
+   3 |   3
+(3 rows)
+
+SELECT NEXTVAL( 'test_sequence' );
+ nextval 
+---------
+       3
+(1 row)
+
+DROP TABLE global_temp_table;
+DROP SEQUENCE test_sequence;
+-- Test two phase commit
+CREATE TABLE global_temptest_persistent(col int);
+CREATE GLOBAL TEMP TABLE global_temptest(col int);
+INSERT INTO global_temptest VALUES (1);
+BEGIN;
+INSERT INTO global_temptest VALUES (2);
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+   2
+(2 rows)
+
+PREPARE TRANSACTION 'global_temp1';
+ERROR:  prepared transactions are disabled
+HINT:  Set max_prepared_transactions to a nonzero value.
+-- We can't see anything from an uncommitted transaction
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+(1 row)
+
+BEGIN;
+INSERT INTO global_temptest VALUES (3);
+INSERT INTO global_temptest_persistent SELECT * FROM global_temptest;
+PREPARE TRANSACTION 'global_temp2';
+ERROR:  prepared transactions are disabled
+HINT:  Set max_prepared_transactions to a nonzero value.
+COMMIT PREPARED 'global_temp1';
+ERROR:  prepared transaction with identifier "global_temp1" does not exist
+-- 1, 2
+SELECT * FROM global_temptest;
+ col 
+-----
+   1
+(1 row)
+
+-- Nothing
+SELECT * FROM global_temptest_persistent;
+ col 
+-----
+(0 rows)
+
+\c
+-- The temp table is empty now.
+SELECT * FROM global_temptest;
+ col 
+-----
+(0 rows)
+
+-- Still nothing in global_temptest_persistent table;
+SELECT * FROM global_temptest_persistent;
+ col 
+-----
+(0 rows)
+
+INSERT INTO global_temptest VALUES (4);
+COMMIT PREPARED 'global_temp2';
+ERROR:  prepared transaction with identifier "global_temp2" does not exist
+-- Only 4
+SELECT * FROM global_temptest;
+ col 
+-----
+   4
+(1 row)
+
+-- 1, 3
+SELECT * FROM global_temptest_persistent;
+ col 
+-----
+(0 rows)
+
+\c
+DROP TABLE global_temptest;
+DROP TABLE global_temptest_persistent;
diff --git a/src/test/regress/expected/session_table.out b/src/test/regress/expected/session_table.out
new file mode 100644
index 0000000..1b9b3f4
--- /dev/null
+++ b/src/test/regress/expected/session_table.out
@@ -0,0 +1,64 @@
+create session table my_private_table(x integer primary key, y integer);
+insert into my_private_table values (generate_series(1,10000), generate_series(1,10000));
+select count(*) from my_private_table;
+ count 
+-------
+ 10000
+(1 row)
+
+\c
+select count(*) from my_private_table;
+ count 
+-------
+     0
+(1 row)
+
+select * from my_private_table where x=10001;
+ x | y 
+---+---
+(0 rows)
+
+insert into my_private_table values (generate_series(1,100000), generate_series(1,100000));
+create index on my_private_table(y);
+select * from my_private_table where x=10001;
+   x   |   y   
+-------+-------
+ 10001 | 10001
+(1 row)
+
+select * from my_private_table where y=10001;
+   x   |   y   
+-------+-------
+ 10001 | 10001
+(1 row)
+
+select count(*) from my_private_table;
+ count  
+--------
+ 100000
+(1 row)
+
+\c
+select * from my_private_table where x=100001;
+ x | y 
+---+---
+(0 rows)
+
+select * from my_private_table order by y desc limit 1;
+ x | y 
+---+---
+(0 rows)
+
+insert into my_private_table values (generate_series(1,100000), generate_series(1,100000));
+select * from my_private_table where x=100001;
+ x | y 
+---+---
+(0 rows)
+
+select * from my_private_table order by y desc limit 1;
+   x    |   y    
+--------+--------
+ 100000 | 100000
+(1 row)
+
+drop table  my_private_table;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index fc0f141..507cf7d 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -107,7 +107,7 @@ test: json jsonb json_encoding jsonpath jsonpath_encoding jsonb_jsonpath
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
 # so keep this parallel group to at most 19 tests
 # ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp global_temp session_table domain rangefuncs prepare conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
 
 # ----------
 # Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 68ac56a..3890777 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -172,6 +172,8 @@ test: limit
 test: plpgsql
 test: copy2
 test: temp
+test: global_temp
+test: session_table
 test: domain
 test: rangefuncs
 test: prepare
diff --git a/src/test/regress/sql/global_temp.sql b/src/test/regress/sql/global_temp.sql
new file mode 100644
index 0000000..4d2da8d
--- /dev/null
+++ b/src/test/regress/sql/global_temp.sql
@@ -0,0 +1,191 @@
+--
+-- GLOBAL TEMP
+-- Test global temp relations
+--
+
+-- Test ON COMMIT DELETE ROWS
+
+CREATE GLOBAL TEMP TABLE global_temptest(col int) ON COMMIT DELETE ROWS;
+
+BEGIN;
+INSERT INTO global_temptest VALUES (1);
+INSERT INTO global_temptest VALUES (2);
+
+SELECT * FROM global_temptest;
+COMMIT;
+
+SELECT * FROM global_temptest;
+
+DROP TABLE global_temptest;
+
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temptest(col) ON COMMIT DELETE ROWS AS SELECT 1;
+
+SELECT * FROM global_temptest;
+COMMIT;
+
+SELECT * FROM global_temptest;
+
+DROP TABLE global_temptest;
+
+-- Test foreign keys
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temptest1(col int PRIMARY KEY);
+CREATE GLOBAL TEMP TABLE global_temptest2(col int REFERENCES global_temptest1)
+  ON COMMIT DELETE ROWS;
+INSERT INTO global_temptest1 VALUES (1);
+INSERT INTO global_temptest2 VALUES (1);
+COMMIT;
+SELECT * FROM global_temptest1;
+SELECT * FROM global_temptest2;
+
+DROP TABLE global_temptest2;
+DROP TABLE global_temptest1;
+
+-- Unsupported ON COMMIT and foreign key combination
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temptest3(col int PRIMARY KEY) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temptest4(col int REFERENCES global_temptest3);
+COMMIT;
+
+-- For partitioned temp tables, ON COMMIT actions ignore storage-less
+-- partitioned tables.
+BEGIN;
+CREATE GLOBAL TEMP TABLE temp_parted_oncommit (a int)
+  PARTITION BY LIST (a) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE temp_parted_oncommit_1
+  PARTITION OF temp_parted_oncommit
+  FOR VALUES IN (1) ON COMMIT DELETE ROWS;
+INSERT INTO temp_parted_oncommit VALUES (1);
+COMMIT;
+-- partitions are emptied by the previous commit
+SELECT * FROM temp_parted_oncommit;
+DROP TABLE temp_parted_oncommit;
+
+-- Using ON COMMIT DELETE on a partitioned table does not remove
+-- all rows if partitions preserve their data.
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temp_parted_oncommit_test (a int)
+  PARTITION BY LIST (a) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temp_parted_oncommit_test1
+  PARTITION OF global_temp_parted_oncommit_test
+  FOR VALUES IN (1) ON COMMIT PRESERVE ROWS;
+INSERT INTO global_temp_parted_oncommit_test VALUES (1);
+COMMIT;
+-- Data from the remaining partition is still here as its rows are
+-- preserved.
+SELECT * FROM global_temp_parted_oncommit_test;
+-- two relations remain in this case.
+SELECT relname FROM pg_class WHERE relname LIKE 'global_temp_parted_oncommit_test%';
+DROP TABLE global_temp_parted_oncommit_test;
+
+-- Check dependencies between ON COMMIT actions with inheritance trees.
+-- Data on the parent is removed, and the child goes away.
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temp_inh_oncommit_test (a int) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temp_inh_oncommit_test1 ()
+  INHERITS(global_temp_inh_oncommit_test) ON COMMIT PRESERVE ROWS;
+INSERT INTO global_temp_inh_oncommit_test1 VALUES (1);
+INSERT INTO global_temp_inh_oncommit_test VALUES (1);
+COMMIT;
+SELECT * FROM global_temp_inh_oncommit_test;
+-- two relations remain
+SELECT relname FROM pg_class WHERE relname LIKE 'global_temp_inh_oncommit_test%';
+DROP TABLE global_temp_inh_oncommit_test1;
+DROP TABLE global_temp_inh_oncommit_test;
+
+-- Global temp table cannot inherit from temporary relation
+BEGIN;
+CREATE TEMP TABLE global_temp_table (a int) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMP TABLE global_temp_table1 ()
+  INHERITS(global_temp_table) ON COMMIT PRESERVE ROWS;
+ROLLBACK;
+
+-- Temp table can inherit from global temporary relation
+BEGIN;
+CREATE GLOBAL TEMP TABLE global_temp_table (a int) ON COMMIT DELETE ROWS;
+CREATE TEMP TABLE temp_table1 ()
+  INHERITS(global_temp_table) ON COMMIT PRESERVE ROWS;
+CREATE TEMP TABLE temp_table2 ()
+  INHERITS(global_temp_table) ON COMMIT DELETE ROWS;
+INSERT INTO temp_table2 VALUES (2);
+INSERT INTO temp_table1 VALUES (1);
+INSERT INTO global_temp_table VALUES (0);
+SELECT * FROM global_temp_table;
+COMMIT;
+SELECT * FROM global_temp_table;
+DROP TABLE temp_table2;
+DROP TABLE temp_table1;
+DROP TABLE global_temp_table;
+
+-- Global temp table can inherit from normal relation
+BEGIN;
+CREATE TABLE normal_table (a int);
+CREATE GLOBAL TEMP TABLE temp_table1 ()
+  INHERITS(normal_table) ON COMMIT PRESERVE ROWS;
+CREATE GLOBAL TEMP TABLE temp_table2 ()
+  INHERITS(normal_table) ON COMMIT DELETE ROWS;
+INSERT INTO temp_table2 VALUES (2);
+INSERT INTO temp_table1 VALUES (1);
+INSERT INTO normal_table VALUES (0);
+SELECT * FROM normal_table;
+COMMIT;
+SELECT * FROM normal_table;
+DROP TABLE temp_table2;
+DROP TABLE temp_table1;
+DROP TABLE normal_table;
+
+-- Check SERIAL and BIGSERIAL pseudo-types
+CREATE GLOBAL TEMP TABLE global_temp_table ( aid BIGSERIAL, bid SERIAL );
+CREATE SEQUENCE test_sequence;
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+SELECT * FROM global_temp_table;
+SELECT NEXTVAL( 'test_sequence' );
+\c
+SELECT * FROM global_temp_table;
+SELECT NEXTVAL( 'test_sequence' );
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+INSERT INTO global_temp_table DEFAULT VALUES;
+SELECT * FROM global_temp_table;
+SELECT NEXTVAL( 'test_sequence' );
+DROP TABLE global_temp_table;
+DROP SEQUENCE test_sequence;
+
+-- Test two phase commit
+CREATE TABLE global_temptest_persistent(col int);
+CREATE GLOBAL TEMP TABLE global_temptest(col int);
+INSERT INTO global_temptest VALUES (1);
+
+BEGIN;
+INSERT INTO global_temptest VALUES (2);
+SELECT * FROM global_temptest;
+PREPARE TRANSACTION 'global_temp1';
+-- We can't see anything from an uncommitted transaction
+SELECT * FROM global_temptest;
+
+BEGIN;
+INSERT INTO global_temptest VALUES (3);
+INSERT INTO global_temptest_persistent SELECT * FROM global_temptest;
+PREPARE TRANSACTION 'global_temp2';
+COMMIT PREPARED 'global_temp1';
+-- 1, 2
+SELECT * FROM global_temptest;
+-- Nothing
+SELECT * FROM global_temptest_persistent;
+\c
+-- The temp table is empty now.
+SELECT * FROM global_temptest;
+-- Still nothing in global_temptest_persistent table;
+SELECT * FROM global_temptest_persistent;
+INSERT INTO global_temptest VALUES (4);
+COMMIT PREPARED 'global_temp2';
+-- Only 4
+SELECT * FROM global_temptest;
+-- 1, 3
+SELECT * FROM global_temptest_persistent;
+\c
+DROP TABLE global_temptest;
+DROP TABLE global_temptest_persistent;
diff --git a/src/test/regress/sql/session_table.sql b/src/test/regress/sql/session_table.sql
new file mode 100644
index 0000000..c6663dc
--- /dev/null
+++ b/src/test/regress/sql/session_table.sql
@@ -0,0 +1,18 @@
+create session table my_private_table(x integer primary key, y integer);
+insert into my_private_table values (generate_series(1,10000), generate_series(1,10000));
+select count(*) from my_private_table;
+\c
+select count(*) from my_private_table;
+select * from my_private_table where x=10001;
+insert into my_private_table values (generate_series(1,100000), generate_series(1,100000));
+create index on my_private_table(y);
+select * from my_private_table where x=10001;
+select * from my_private_table where y=10001;
+select count(*) from my_private_table;
+\c
+select * from my_private_table where x=100001;
+select * from my_private_table order by y desc limit 1;
+insert into my_private_table values (generate_series(1,100000), generate_series(1,100000));
+select * from my_private_table where x=100001;
+select * from my_private_table order by y desc limit 1;
+drop table  my_private_table;

Reply via email to