0003-gtt-v65-implementation.patch

application/octet-stream

Filename: 0003-gtt-v65-implementation.patch
Type: application/octet-stream
Part: 2
Message: Re: [Proposal] Global temporary tables

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v65-0003
File+
contrib/amcheck/verify_heapam.c 8 0
src/backend/access/common/reloptions.c 14 0
src/backend/access/gist/gistutil.c 1 1
src/backend/access/hash/hash.c 1 1
src/backend/access/heap/heapam.c 1 0
src/backend/access/heap/heapam_handler.c 2 2
src/backend/access/heap/vacuumlazy.c 3 0
src/backend/access/nbtree/nbtpage.c 9 0
src/backend/catalog/catalog.c 1 0
src/backend/catalog/heap.c 91 21
src/backend/catalog/index.c 95 13
src/backend/catalog/Makefile 1 0
src/backend/catalog/namespace.c 7 0
src/backend/catalog/storage.c 59 3
src/backend/catalog/storage_gtt.c 1669 0
src/backend/catalog/system_views.sql 85 0
src/backend/commands/analyze.c 54 24
src/backend/commands/cluster.c 21 1
src/backend/commands/copy.c 1 1
src/backend/commands/copyfrom.c 2 1
src/backend/commands/createas.c 7 0
src/backend/commands/indexcmds.c 58 21
src/backend/commands/lockcmds.c 23 2
src/backend/commands/sequence.c 82 13
src/backend/commands/tablecmds.c 196 15
src/backend/commands/vacuum.c 63 1
src/backend/commands/view.c 6 0
src/backend/executor/execMain.c 4 0
src/backend/executor/execUtils.c 5 1
src/backend/executor/nodeModifyTable.c 4 3
src/backend/optimizer/path/allpaths.c 2 2
src/backend/optimizer/plan/planner.c 1 1
src/backend/optimizer/util/plancat.c 9 0
src/backend/parser/analyze.c 1 1
src/backend/parser/gram.y 4 16
src/backend/parser/parse_relation.c 50 0
src/backend/parser/parse_utilcmd.c 9 0
src/backend/postmaster/autovacuum.c 9 1
src/backend/storage/buffer/bufmgr.c 10 1
src/backend/storage/ipc/ipci.c 4 0
src/backend/storage/ipc/procarray.c 64 0
src/backend/storage/lmgr/lwlock.c 3 1
src/backend/storage/lmgr/proc.c 2 0
src/backend/utils/adt/dbsize.c 7 0
src/backend/utils/adt/selfuncs.c 84 15
src/backend/utils/cache/lsyscache.c 14 0
src/backend/utils/cache/relcache.c 77 2
src/backend/utils/misc/guc.c 32 0
src/bin/pg_dump/pg_dump.c 40 9
src/bin/pg_upgrade/check.c 2 2
src/bin/pg_upgrade/info.c 16 5
src/bin/pg_upgrade/pg_upgrade.c 9 3
src/bin/pg_upgrade/pg_upgrade.h 1 1
src/bin/psql/describe.c 2 1
src/bin/psql/tab-complete.c 7 0
src/include/catalog/heap.h 3 3
src/include/catalog/index.h 1 1
src/include/catalog/pg_class.h 1 0
src/include/catalog/pg_proc.dat 34 0
src/include/catalog/storage_gtt.h 46 0
src/include/catalog/storage.h 1 1
src/include/commands/sequence.h 1 0
src/include/commands/tablecmds.h 1 1
src/include/executor/executor.h 1 1
src/include/parser/parse_relation.h 1 0
src/include/storage/lwlock.h 1 0
src/include/storage/procarray.h 2 0
src/include/storage/proc.h 2 0
src/include/utils/guc.h 3 0
src/include/utils/rel.h 44 3
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index f996f9a5727..18f1f7a42c0 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -18,6 +18,7 @@
 #include "access/toast_internals.h"
 #include "access/visibilitymap.h"
 #include "catalog/pg_am.h"
+#include "catalog/storage_gtt.h"
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
@@ -340,6 +341,13 @@ verify_heapam(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 	}
 
+	if (RELATION_IS_GLOBAL_TEMP(ctx.rel) &&
+		!gtt_storage_attached(RelationGetRelid(ctx.rel)))
+	{
+		relation_close(ctx.rel, AccessShareLock);
+		PG_RETURN_NULL();
+	}
+
 	/* Early exit if the relation is empty */
 	nblocks = RelationGetNumberOfBlocks(ctx.rel);
 	if (!nblocks)
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index d592655258a..051fe802824 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -159,6 +159,18 @@ static relopt_bool boolRelOpts[] =
 		},
 		true
 	},
+	/*
+	 * For global temporary table save on commit clause info to reloptions.
+	 */
+	{
+		{
+			"on_commit_delete_rows",
+			"global temporary table on commit options",
+			RELOPT_KIND_HEAP,
+			ShareUpdateExclusiveLock
+		},
+		true
+	},
 	/* list terminator */
 	{{NULL}}
 };
@@ -1834,6 +1846,8 @@ bytea *
 default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 {
 	static const relopt_parse_elt tab[] = {
+		{"on_commit_delete_rows", RELOPT_TYPE_BOOL,
+		offsetof(StdRdOptions, on_commit_delete_rows)},
 		{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
 		{"autovacuum_enabled", RELOPT_TYPE_BOOL,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index d4bf0c7563d..b623e958c38 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -1024,7 +1024,7 @@ gistproperty(Oid index_oid, int attno,
 XLogRecPtr
 gistGetFakeLSN(Relation rel)
 {
-	if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+	if (RELATION_IS_TEMP(rel))
 	{
 		/*
 		 * Temporary relations are only accessible in our session, so a simple
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index d48c8a45498..a6afb55fbc0 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -152,7 +152,7 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	 * metapage, nor the first bitmap page.
 	 */
 	sort_threshold = (maintenance_work_mem * 1024L) / BLCKSZ;
-	if (index->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
+	if (!RELATION_IS_TEMP(index))
 		sort_threshold = Min(sort_threshold, NBuffers);
 	else
 		sort_threshold = Min(sort_threshold, NLocBuffer);
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 6ec57f3d8b2..7a143fe545a 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -5835,6 +5835,7 @@ heap_abort_speculative(Relation relation, ItemPointer tid)
 	Buffer		buffer;
 	TransactionId prune_xid;
 
+	Assert(TransactionIdIsNormal(relation->rd_rel->relfrozenxid));
 	Assert(ItemPointerIsValid(tid));
 
 	block = ItemPointerGetBlockNumber(tid);
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 39ef8a0b77d..0bb308d1c5e 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -593,7 +593,7 @@ heapam_relation_set_new_filenode(Relation rel,
 	 */
 	*minmulti = GetOldestMultiXactId();
 
-	srel = RelationCreateStorage(*newrnode, persistence);
+	srel = RelationCreateStorage(*newrnode, persistence, rel);
 
 	/*
 	 * If required, set up an init fork for an unlogged table so that it can
@@ -645,7 +645,7 @@ heapam_relation_copy_data(Relation rel, const RelFileNode *newrnode)
 	 * NOTE: any conflict in relfilenode value will be caught in
 	 * RelationCreateStorage().
 	 */
-	RelationCreateStorage(*newrnode, rel->rd_rel->relpersistence);
+	RelationCreateStorage(*newrnode, rel->rd_rel->relpersistence, rel);
 
 	/* copy main fork */
 	RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 1749cc2a476..de5914db777 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -324,6 +324,9 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
 	TransactionId FreezeLimit;
 	MultiXactId MultiXactCutoff;
 
+	Assert(TransactionIdIsNormal(rel->rd_rel->relfrozenxid));
+        Assert(MultiXactIdIsValid(rel->rd_rel->relminmxid));
+
 	verbose = (params->options & VACOPT_VERBOSE) != 0;
 	instrument = (verbose || (IsAutoVacuumWorkerProcess() &&
 							  params->log_min_duration >= 0));
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 6b5f01e1d07..099f942c936 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -28,6 +28,7 @@
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
+#include "catalog/storage_gtt.h"
 #include "miscadmin.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
@@ -677,6 +678,14 @@ _bt_getrootheight(Relation rel)
 	{
 		Buffer		metabuf;
 
+		/*
+		 * If a global temporary table storage file is not initialized in the
+		 * this session, its index does not have a root page, just returns 0.
+		 */
+		if (RELATION_IS_GLOBAL_TEMP(rel) &&
+			!gtt_storage_attached(RelationGetRelid(rel)))
+			return 0;
+
 		metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
 		metad = _bt_getmeta(rel, metabuf);
 
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index df5268fbc30..8b8a60c2ac5 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -44,6 +44,7 @@ OBJS = \
 	pg_subscription.o \
 	pg_type.o \
 	storage.o \
+	storage_gtt.o \
 	toasting.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index dfd5fb669ee..c0fd68652f7 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -504,6 +504,7 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 	switch (relpersistence)
 	{
 		case RELPERSISTENCE_TEMP:
+		case RELPERSISTENCE_GLOBAL_TEMP:
 			backend = BackendIdForTempRelations();
 			break;
 		case RELPERSISTENCE_UNLOGGED:
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 7e99de88b34..019e1105bc4 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -62,6 +62,7 @@
 #include "catalog/pg_type.h"
 #include "catalog/storage.h"
 #include "catalog/storage_xlog.h"
+#include "catalog/storage_gtt.h"
 #include "commands/tablecmds.h"
 #include "commands/typecmds.h"
 #include "executor/executor.h"
@@ -102,6 +103,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 								Oid reloftype,
 								Oid relowner,
 								char relkind,
+								char relpersistence,
 								TransactionId relfrozenxid,
 								TransactionId relminmxid,
 								Datum relacl,
@@ -349,8 +351,21 @@ heap_create(const char *relname,
 	if (!RELKIND_HAS_TABLESPACE(relkind))
 		reltablespace = InvalidOid;
 
+	/*
+	 * For create global temporary table, initialization storage information
+	 * and recorded in into pg_class, but not initialization stroage file.
+	 * When data is inserted into a temporary table, its storage file is initialized.
+	 */
+	if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		create_storage = false;
+		if (OidIsValid(relfilenode))
+			elog(ERROR, "global temporary table can not reuse an existing relfilenode");
+
+		relfilenode = relid;
+	}
 	/* Don't create storage for relkinds without physical storage. */
-	if (!RELKIND_HAS_STORAGE(relkind))
+	else if (!RELKIND_HAS_STORAGE(relkind))
 		create_storage = false;
 	else
 	{
@@ -403,7 +418,7 @@ heap_create(const char *relname,
 											relpersistence,
 											relfrozenxid, relminmxid);
 		else if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
-			RelationCreateStorage(rel->rd_node, relpersistence);
+			RelationCreateStorage(rel->rd_node, relpersistence, rel);
 		else
 			Assert(false);
 	}
@@ -970,6 +985,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					char relpersistence,
 					TransactionId relfrozenxid,
 					TransactionId relminmxid,
 					Datum relacl,
@@ -995,8 +1011,21 @@ AddNewRelationTuple(Relation pg_class_desc,
 		new_rel_reltup->reltuples = 1;
 	}
 
-	new_rel_reltup->relfrozenxid = relfrozenxid;
-	new_rel_reltup->relminmxid = relminmxid;
+	/*
+	 * The transaction information of the global temporary table is stored
+	 * in hash table, not in pg_class.
+	 */
+	if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		new_rel_reltup->relfrozenxid = InvalidTransactionId;
+		new_rel_reltup->relminmxid = InvalidMultiXactId;
+	}
+	else
+	{
+		new_rel_reltup->relfrozenxid = relfrozenxid;
+		new_rel_reltup->relminmxid = relminmxid;
+	}
+
 	new_rel_reltup->relowner = relowner;
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->reloftype = reloftype;
@@ -1404,6 +1433,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relpersistence,
 						relfrozenxid,
 						relminmxid,
 						PointerGetDatum(relacl),
@@ -1486,10 +1516,13 @@ heap_create_with_catalog(const char *relname,
 	StoreConstraints(new_rel_desc, cooked_constraints, is_internal);
 
 	/*
-	 * If there's a special on-commit action, remember it
+	 * For local temporary table, if there's a special on-commit action, remember it.
+	 * For global temporary table, on-commit action is recorded during initial storage.
+	 * See remember_gtt_storage_info.
 	 */
-	if (oncommit != ONCOMMIT_NOOP)
-		register_on_commit_action(relid, oncommit);
+	if (oncommit != ONCOMMIT_NOOP &&
+		relpersistence != RELPERSISTENCE_GLOBAL_TEMP)
+		register_on_commit_action(relid, oncommit, false);
 
 	/*
 	 * ok, the relation has been cataloged, so close our relations and return
@@ -1986,6 +2019,13 @@ heap_drop_with_catalog(Oid relid)
 	if (relid == defaultPartOid)
 		update_default_partition_oid(parentOid, InvalidOid);
 
+	/*
+	 * Only when other sessions are not using this global temporary table,
+	 * is it allowed to drop it.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+		CheckGlobalTempTableNotInUse(rel, "DROP GLOBAL TEMPORARY TABLE");
+
 	/*
 	 * Schedule unlinking of the relation's physical files at commit.
 	 */
@@ -3272,7 +3312,7 @@ RemoveStatistics(Oid relid, AttrNumber attnum)
  * the specified relation.  Caller must hold exclusive lock on rel.
  */
 static void
-RelationTruncateIndexes(Relation heapRelation)
+RelationTruncateIndexes(Relation heapRelation, LOCKMODE lockmode)
 {
 	ListCell   *indlist;
 
@@ -3284,7 +3324,7 @@ RelationTruncateIndexes(Relation heapRelation)
 		IndexInfo  *indexInfo;
 
 		/* Open the index relation; use exclusive lock, just to be sure */
-		currentIndex = index_open(indexId, AccessExclusiveLock);
+		currentIndex = index_open(indexId, lockmode);
 
 		/*
 		 * Fetch info needed for index_build.  Since we know there are no
@@ -3320,31 +3360,49 @@ RelationTruncateIndexes(Relation heapRelation)
  * ON COMMIT truncation of temporary tables, where it doesn't matter.
  */
 void
-heap_truncate(List *relids)
+heap_truncate(List *relids, bool is_global_temp)
 {
 	List	   *relations = NIL;
-	ListCell   *cell;
+	List	   *lock_modes = NIL;
+	ListCell   *cell_rel;
+	ListCell   *cell_lock;
 
 	/* Open relations for processing, and grab exclusive access on each */
-	foreach(cell, relids)
+	foreach(cell_rel, relids)
 	{
-		Oid			rid = lfirst_oid(cell);
+		Oid			rid = lfirst_oid(cell_rel);
 		Relation	rel;
+		LOCKMODE	lockmode;
+
+		/*
+		 * Truncate global temporary table only clears local data,
+		 * so only low-level locks need to be held.
+		 */
+		if (is_global_temp)
+		{
+			lockmode = RowExclusiveLock;
+			if (!gtt_storage_attached(rid))
+				continue;
+		}
+		else
+			lockmode = AccessExclusiveLock;
 
-		rel = table_open(rid, AccessExclusiveLock);
+		rel = table_open(rid, lockmode);
 		relations = lappend(relations, rel);
+		lock_modes = lappend_int(lock_modes, lockmode);
 	}
 
 	/* Don't allow truncate on tables that are referenced by foreign keys */
 	heap_truncate_check_FKs(relations, true);
 
 	/* OK to do it */
-	foreach(cell, relations)
+	forboth(cell_rel, relations, cell_lock, lock_modes)
 	{
-		Relation	rel = lfirst(cell);
+		Relation	rel = lfirst(cell_rel);
+		LOCKMODE	lockmode = lfirst_int(cell_lock);
 
 		/* Truncate the relation */
-		heap_truncate_one_rel(rel);
+		heap_truncate_one_rel(rel, lockmode);
 
 		/* Close the relation, but keep exclusive lock on it until commit */
 		table_close(rel, NoLock);
@@ -3361,7 +3419,7 @@ heap_truncate(List *relids)
  * checked permissions etc, and must have obtained AccessExclusiveLock.
  */
 void
-heap_truncate_one_rel(Relation rel)
+heap_truncate_one_rel(Relation rel, LOCKMODE lockmode)
 {
 	Oid			toastrelid;
 
@@ -3376,16 +3434,16 @@ heap_truncate_one_rel(Relation rel)
 	table_relation_nontransactional_truncate(rel);
 
 	/* If the relation has indexes, truncate the indexes too */
-	RelationTruncateIndexes(rel);
+	RelationTruncateIndexes(rel, lockmode);
 
 	/* If there is a toast table, truncate that too */
 	toastrelid = rel->rd_rel->reltoastrelid;
 	if (OidIsValid(toastrelid))
 	{
-		Relation	toastrel = table_open(toastrelid, AccessExclusiveLock);
+		Relation	toastrel = table_open(toastrelid, lockmode);
 
 		table_relation_nontransactional_truncate(toastrel);
-		RelationTruncateIndexes(toastrel);
+		RelationTruncateIndexes(toastrel, lockmode);
 		/* keep the lock... */
 		table_close(toastrel, NoLock);
 	}
@@ -3856,3 +3914,15 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
 
 	CacheInvalidateRelcache(parent);
 }
+
+void
+CheckGlobalTempTableNotInUse(Relation rel, const char *stmt)
+{
+	if (RELATION_IS_GLOBAL_TEMP(rel) &&
+		is_other_backend_use_gtt(RelationGetRelid(rel)))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_IN_USE),
+				 errmsg("cannot %s \"%s\" because it is being used by other session",
+						stmt, RelationGetRelationName(rel)),
+				 errdetail("Please try detach all other sessions using this table and try again.")));
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index e4203819a0e..17b79212365 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -54,6 +54,7 @@
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "catalog/storage.h"
+#include "catalog/storage_gtt.h"
 #include "commands/event_trigger.h"
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
@@ -125,7 +126,8 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
 								bool isready);
 static void index_update_stats(Relation rel,
 							   bool hasindex,
-							   double reltuples);
+							   double reltuples,
+							   bool isreindex);
 static void IndexCheckExclusion(Relation heapRelation,
 								Relation indexRelation,
 								IndexInfo *indexInfo);
@@ -736,6 +738,21 @@ index_create(Relation heapRelation,
 	MultiXactId relminmxid;
 	bool		create_storage = !OidIsValid(relFileNode);
 
+	if (RELATION_IS_GLOBAL_TEMP(heapRelation))
+	{
+		/* disable create index on global temporary table with concurrent mode */
+		concurrent = false;
+
+		/*
+		 * For the case that some backend is applied relcache message to create
+		 * an index on a global temporary table, if this table in the current
+		 * backend are not initialized, the creation of index storage on the
+		 * table are also skipped.
+		 */
+		if (!gtt_storage_attached(RelationGetRelid(heapRelation)))
+			flags |= INDEX_CREATE_SKIP_BUILD;
+	}
+
 	/* constraint flags can only be set when a constraint is requested */
 	Assert((constr_flags == 0) ||
 		   ((flags & INDEX_CREATE_ADD_CONSTRAINT) != 0));
@@ -1242,7 +1259,8 @@ index_create(Relation heapRelation,
 		 */
 		index_update_stats(heapRelation,
 						   true,
-						   -1.0);
+						   -1.0,
+						   false);
 		/* Make the above update visible */
 		CommandCounterIncrement();
 	}
@@ -2138,7 +2156,7 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode)
 	 * lock (see comments in RemoveRelations), and a non-concurrent DROP is
 	 * more efficient.
 	 */
-	Assert(get_rel_persistence(indexId) != RELPERSISTENCE_TEMP ||
+	Assert(!RelpersistenceTsTemp(get_rel_persistence(indexId)) ||
 		   (!concurrent && !concurrent_lock_mode));
 
 	/*
@@ -2170,6 +2188,14 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode)
 	 */
 	CheckTableNotInUse(userIndexRelation, "DROP INDEX");
 
+	/*
+	 * Allow to drop index on global temporary table when only current
+	 * backend use it.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(userHeapRelation))
+		CheckGlobalTempTableNotInUse(userHeapRelation,
+									 "DROP INDEX ON GLOBAL TEMPORARY TABLE");
+
 	/*
 	 * Drop Index Concurrently is more or less the reverse process of Create
 	 * Index Concurrently.
@@ -2771,7 +2797,8 @@ FormIndexDatum(IndexInfo *indexInfo,
 static void
 index_update_stats(Relation rel,
 				   bool hasindex,
-				   double reltuples)
+				   double reltuples,
+				   bool isreindex)
 {
 	Oid			relid = RelationGetRelid(rel);
 	Relation	pg_class;
@@ -2779,6 +2806,13 @@ index_update_stats(Relation rel,
 	Form_pg_class rd_rel;
 	bool		dirty;
 
+	/*
+	 * Most of the global Temp table data is updated to the local hash, and reindex
+	 * does not refresh relcache, so call a separate function.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+		return index_update_gtt_relstats(rel, hasindex, reltuples, isreindex);
+
 	/*
 	 * We always update the pg_class row using a non-transactional,
 	 * overwrite-in-place update.  There are several reasons for this:
@@ -2998,6 +3032,25 @@ index_build(Relation heapRelation,
 		pgstat_progress_update_multi_param(6, progress_index, progress_vals);
 	}
 
+	/* For build index on global temporary table */
+	if (RELATION_IS_GLOBAL_TEMP(indexRelation))
+	{
+		/*
+		 * If the storage for the index in this session is not initialized,
+		 * it needs to be created.
+		 */
+		if (!gtt_storage_attached(RelationGetRelid(indexRelation)))
+		{
+			/* Before create init storage, fix the local Relcache first */
+			force_enable_gtt_index(indexRelation);
+
+			Assert(gtt_storage_attached(RelationGetRelid(heapRelation)));
+
+			/* Init storage for index */
+			RelationCreateStorage(indexRelation->rd_node, RELPERSISTENCE_GLOBAL_TEMP, indexRelation);
+		}
+	}
+
 	/*
 	 * Call the access method's build procedure
 	 */
@@ -3080,11 +3133,13 @@ index_build(Relation heapRelation,
 	 */
 	index_update_stats(heapRelation,
 					   true,
-					   stats->heap_tuples);
+					   stats->heap_tuples,
+					   isreindex);
 
 	index_update_stats(indexRelation,
 					   false,
-					   stats->index_tuples);
+					   stats->index_tuples,
+					   isreindex);
 
 	/* Make the updated catalog row versions visible */
 	CommandCounterIncrement();
@@ -3539,6 +3594,8 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
 	PGRUsage	ru0;
 	bool		progress = ((params->options & REINDEXOPT_REPORT_PROGRESS) != 0);
 	bool		set_tablespace = false;
+	LOCKMODE	lockmode_on_heap = ShareLock;
+	LOCKMODE	lockmode_on_index = AccessExclusiveLock;
 
 	pg_rusage_init(&ru0);
 
@@ -3552,10 +3609,35 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
 	if (!OidIsValid(heapId))
 		return;
 
+	/*
+	 * For reindex on global temporary table, If the storage for the index
+	 * in current session is not initialized, nothing is done.
+	 */
+	if (persistence == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		if (OidIsValid(params->tablespaceOid))
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot change tablespace of global temporary table")));
+
+		if (!gtt_storage_attached(indexId))
+		{
+			/* Suppress use of the target index while rebuilding it */
+			SetReindexProcessing(heapId, indexId);
+			/* Re-allow use of target index */
+			ResetReindexProcessing();
+			return;
+		}
+
+		/* For global temp table reindex handles local data, using low-level locks */
+		lockmode_on_heap = AccessShareLock;
+		lockmode_on_index = AccessShareLock;
+	}
+
 	if ((params->options & REINDEXOPT_MISSING_OK) != 0)
-		heapRelation = try_table_open(heapId, ShareLock);
+		heapRelation = try_table_open(heapId, lockmode_on_heap);
 	else
-		heapRelation = table_open(heapId, ShareLock);
+		heapRelation = table_open(heapId, lockmode_on_heap);
 
 	/* if relation is gone, leave */
 	if (!heapRelation)
@@ -3581,7 +3663,7 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
 	 * Open the target index relation and get an exclusive lock on it, to
 	 * ensure that no one else is touching this particular index.
 	 */
-	iRel = index_open(indexId, AccessExclusiveLock);
+	iRel = index_open(indexId, lockmode_on_index);
 
 	if (progress)
 		pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID,
@@ -3823,7 +3905,7 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
  * index rebuild.
  */
 bool
-reindex_relation(Oid relid, int flags, ReindexParams *params)
+reindex_relation(Oid relid, int flags, ReindexParams *params, LOCKMODE lockmode)
 {
 	Relation	rel;
 	Oid			toast_relid;
@@ -3839,9 +3921,9 @@ reindex_relation(Oid relid, int flags, ReindexParams *params)
 	 * should match ReindexTable().
 	 */
 	if ((params->options & REINDEXOPT_MISSING_OK) != 0)
-		rel = try_table_open(relid, ShareLock);
+		rel = try_table_open(relid, lockmode);
 	else
-		rel = table_open(relid, ShareLock);
+		rel = table_open(relid, lockmode);
 
 	/* if relation is gone, leave */
 	if (!rel)
@@ -3948,7 +4030,7 @@ reindex_relation(Oid relid, int flags, ReindexParams *params)
 
 		newparams.options &= ~(REINDEXOPT_MISSING_OK);
 		newparams.tablespaceOid = InvalidOid;
-		result |= reindex_relation(toast_relid, flags, &newparams);
+		result |= reindex_relation(toast_relid, flags, &newparams, lockmode);
 	}
 
 	return result;
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 5dbac9c437a..033fef26fe3 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -656,6 +656,13 @@ RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid)
 							 errmsg("cannot create temporary relation in non-temporary schema")));
 			}
 			break;
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			/* Do not allow create global temporary table in temporary schemas */
+			if (isAnyTempNamespace(nspid))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("cannot create global temporary relation in temporary schema")));
+			break;
 		case RELPERSISTENCE_PERMANENT:
 			if (isTempOrTempToastNamespace(nspid))
 				newRelation->relpersistence = RELPERSISTENCE_TEMP;
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 9b8075536a7..733d89c0e8a 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -27,10 +27,12 @@
 #include "access/xlogutils.h"
 #include "catalog/storage.h"
 #include "catalog/storage_xlog.h"
+#include "catalog/storage_gtt.h"
 #include "miscadmin.h"
 #include "storage/freespace.h"
 #include "storage/smgr.h"
 #include "utils/hsearch.h"
+#include "utils/inval.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
@@ -61,6 +63,7 @@ typedef struct PendingRelDelete
 {
 	RelFileNode relnode;		/* relation that may need to be deleted */
 	BackendId	backend;		/* InvalidBackendId if not a temp rel */
+	Oid			temprelOid;			/* InvalidOid if not a global temporary rel */
 	bool		atCommit;		/* T=delete at commit; F=delete at abort */
 	int			nestLevel;		/* xact nesting level of request */
 	struct PendingRelDelete *next;	/* linked-list link */
@@ -115,7 +118,7 @@ AddPendingSync(const RelFileNode *rnode)
  * transaction aborts later on, the storage will be destroyed.
  */
 SMgrRelation
-RelationCreateStorage(RelFileNode rnode, char relpersistence)
+RelationCreateStorage(RelFileNode rnode, char relpersistence, Relation rel)
 {
 	PendingRelDelete *pending;
 	SMgrRelation srel;
@@ -126,7 +129,12 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 
 	switch (relpersistence)
 	{
+		/*
+		 * global temporary table and local temporary table use same
+		 * design on storage module.
+		 */
 		case RELPERSISTENCE_TEMP:
+		case RELPERSISTENCE_GLOBAL_TEMP:
 			backend = BackendIdForTempRelations();
 			needs_wal = false;
 			break;
@@ -154,6 +162,7 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 		MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
 	pending->relnode = rnode;
 	pending->backend = backend;
+	pending->temprelOid = InvalidOid;
 	pending->atCommit = false;	/* delete if abort */
 	pending->nestLevel = GetCurrentTransactionNestLevel();
 	pending->next = pendingDeletes;
@@ -165,6 +174,30 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 		AddPendingSync(&rnode);
 	}
 
+	if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		Assert(rel && RELATION_IS_GLOBAL_TEMP(rel));
+
+		/*
+		 * Remember the reloid of global temporary table, which is used for
+		 * transaction commit or rollback.
+		 * see smgrDoPendingDeletes.
+		 */
+		pending->temprelOid = RelationGetRelid(rel);
+
+		/* Remember global temporary table storage info to localhash */
+		remember_gtt_storage_info(rnode, rel);
+
+		/* Make cache invalid and set new relnode to local relcache. */
+		CacheInvalidateRelcache(rel);
+
+		/*
+		 * Make the pg_class row change or relation map change visible.  This will
+		 * cause the relcache entry to get updated, too.
+		 */
+		CommandCounterIncrement();
+	}
+
 	return srel;
 }
 
@@ -201,11 +234,20 @@ RelationDropStorage(Relation rel)
 		MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
 	pending->relnode = rel->rd_node;
 	pending->backend = rel->rd_backend;
+	pending->temprelOid = InvalidOid;
 	pending->atCommit = true;	/* delete if commit */
 	pending->nestLevel = GetCurrentTransactionNestLevel();
 	pending->next = pendingDeletes;
 	pendingDeletes = pending;
 
+	/*
+	 * Remember the reloid of global temporary table, which is used for
+	 * transaction commit or rollback.
+	 * see smgrDoPendingDeletes.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+		pending->temprelOid = RelationGetRelid(rel);
+
 	/*
 	 * NOTE: if the relation was created in this transaction, it will now be
 	 * present in the pending-delete list twice, once with atCommit true and
@@ -618,6 +660,7 @@ smgrDoPendingDeletes(bool isCommit)
 	int			nrels = 0,
 				maxrels = 0;
 	SMgrRelation *srels = NULL;
+	Oid			*reloids = NULL;
 
 	prev = NULL;
 	for (pending = pendingDeletes; pending != NULL; pending = next)
@@ -647,14 +690,18 @@ smgrDoPendingDeletes(bool isCommit)
 				{
 					maxrels = 8;
 					srels = palloc(sizeof(SMgrRelation) * maxrels);
+					reloids = palloc(sizeof(Oid) * maxrels);
 				}
 				else if (maxrels <= nrels)
 				{
 					maxrels *= 2;
 					srels = repalloc(srels, sizeof(SMgrRelation) * maxrels);
+					reloids = repalloc(reloids, sizeof(Oid) * maxrels);
 				}
 
-				srels[nrels++] = srel;
+				srels[nrels] = srel;
+				reloids[nrels] = pending->temprelOid;
+				nrels++;
 			}
 			/* must explicitly free the list entry */
 			pfree(pending);
@@ -664,12 +711,21 @@ smgrDoPendingDeletes(bool isCommit)
 
 	if (nrels > 0)
 	{
+		int	i;
+
 		smgrdounlinkall(srels, nrels, false);
 
-		for (int i = 0; i < nrels; i++)
+		for (i = 0; i < nrels; i++)
+		{
 			smgrclose(srels[i]);
 
+			/* free global temporary table info in localhash */
+			if (gtt_storage_attached(reloids[i]))
+				forget_gtt_storage_info(reloids[i], srels[i]->smgr_rnode.node, isCommit);
+		}
+
 		pfree(srels);
+		pfree(reloids);
 	}
 }
 
diff --git a/src/backend/catalog/storage_gtt.c b/src/backend/catalog/storage_gtt.c
new file mode 100644
index 00000000000..ea349a78072
--- /dev/null
+++ b/src/backend/catalog/storage_gtt.c
@@ -0,0 +1,1669 @@
+/*-------------------------------------------------------------------------
+ *
+ * storage_gtt.c
+ *	  The body implementation of Global Temparary table.
+ *
+ * IDENTIFICATION
+ *	  src/backend/catalog/storage_gtt.c
+ *
+ *	  See src/backend/catalog/GTT_README for Global temparary table's
+ *	  requirements and design.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/amapi.h"
+#include "access/heapam.h"
+#include "access/multixact.h"
+#include "access/visibilitymap.h"
+#include "catalog/catalog.h"
+#include "catalog/index.h"
+#include "catalog/pg_statistic.h"
+#include "catalog/storage.h"
+#include "catalog/storage_gtt.h"
+#include "commands/tablecmds.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/pg_list.h"
+#include "storage/bufmgr.h"
+#include "storage/ipc.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/sinvaladt.h"
+#include "utils/catcache.h"
+#include "utils/guc.h"
+#include "utils/inval.h"
+#include "utils/syscache.h"
+
+/* Copy from bitmapset.c, because gtt used the function in bitmapset.c */
+#define WORDNUM(x)	((x) / BITS_PER_BITMAPWORD)
+#define BITNUM(x)	((x) % BITS_PER_BITMAPWORD)
+
+#define BITMAPSET_SIZE(nwords)	\
+	(offsetof(Bitmapset, words) + (nwords) * sizeof(bitmapword))
+
+static bool gtt_cleaner_exit_registered = false;
+static HTAB *gtt_storage_local_hash = NULL;
+static HTAB *active_gtt_shared_hash = NULL;
+static MemoryContext gtt_info_context = NULL;
+
+/* relfrozenxid of all gtts in the current session */
+static List *gtt_session_relfrozenxid_list = NIL;
+
+int		vacuum_gtt_defer_check_age = 0;
+
+/*
+ * The Global temporary table's shared hash table data structure
+ */
+typedef struct gtt_ctl_data
+{
+	LWLock		lock;
+	int			max_entry;
+	int			entry_size;
+}gtt_ctl_data;
+
+static gtt_ctl_data *gtt_shared_ctl = NULL;
+
+typedef struct gtt_fnode
+{
+	Oid			dbNode;
+	Oid			relNode;
+} gtt_fnode;
+
+/* record this global temporary table in which backends are being used */
+typedef struct
+{
+	gtt_fnode	rnode;
+	Bitmapset	*map;
+	/* bitmap data */
+} gtt_shared_hash_entry;
+
+/*
+ * The Global temporary table's local hash table data structure
+ */
+/* Record the storage information and statistical information of the global temporary table */
+typedef struct
+{
+	Oid			relfilenode;
+	Oid			spcnode;
+
+	/* pg_class relstat */
+	int32		relpages;
+	float4		reltuples;
+	int32		relallvisible;
+	TransactionId relfrozenxid;
+	TransactionId relminmxid;
+
+	/* pg_statistic column stat */
+	int			natts;
+	int			*attnum;
+	HeapTuple	*att_stat_tups;
+} gtt_relfilenode;
+
+typedef struct
+{
+	Oid			relid;
+
+	List		*relfilenode_list;
+
+	char		relkind;
+	bool		on_commit_delete;
+} gtt_local_hash_entry;
+
+static Size action_gtt_shared_hash_entry_size(void);
+static void gtt_storage_checkin(Oid relid);
+static void gtt_storage_checkout(Oid relid, bool isCommit);
+static void gtt_storage_removeall(int code, Datum arg);
+static void insert_gtt_relfrozenxid_to_ordered_list(Oid relfrozenxid);
+static void remove_gtt_relfrozenxid_from_ordered_list(Oid relfrozenxid);
+static void set_gtt_session_relfrozenxid(void);
+static void gtt_free_statistics(gtt_relfilenode *rnode);
+static gtt_relfilenode *gtt_search_relfilenode(gtt_local_hash_entry	*entry, Oid relfilenode, Oid spcnode, bool missing_ok);
+static gtt_local_hash_entry *gtt_search_by_relid(Oid relid, bool missing_ok);
+static Bitmapset *copy_active_gtt_bitmap(Oid relid);
+
+Datum pg_get_gtt_statistics(PG_FUNCTION_ARGS);
+Datum pg_get_gtt_relstats(PG_FUNCTION_ARGS);
+Datum pg_gtt_attached_pid(PG_FUNCTION_ARGS);
+Datum pg_list_gtt_relfrozenxids(PG_FUNCTION_ARGS);
+
+/*
+ * Calculate shared hash table entry size for GTT.
+ */
+static Size
+action_gtt_shared_hash_entry_size(void)
+{
+	int	wordnum;
+	Size	hash_entry_size = 0;
+
+	if (max_active_gtt <= 0)
+		return 0;
+
+	wordnum = WORDNUM(MaxBackends + 1);
+	/* hash entry header size */
+	hash_entry_size += MAXALIGN(sizeof(gtt_shared_hash_entry));
+	/*
+	 * hash entry data size
+	 * this is a bitmap in shared memory, each backend have a bit.
+	 */
+	hash_entry_size += MAXALIGN(BITMAPSET_SIZE(wordnum + 1));
+
+	return hash_entry_size;
+}
+
+/*
+ * Calculate shared hash table max size for GTT.
+ */
+Size
+active_gtt_shared_hash_size(void)
+{
+	Size	size = 0;
+	Size	hash_entry_size = 0;
+
+	if (max_active_gtt <= 0)
+		return 0;
+
+	/* shared hash header size */
+	size = MAXALIGN(sizeof(gtt_ctl_data));
+	/* hash entry size */
+	hash_entry_size = action_gtt_shared_hash_entry_size();
+	/* max size */
+	size += hash_estimate_size(max_active_gtt, hash_entry_size);
+
+	return size;
+}
+
+/*
+ * Initialization shared hash table for GTT.
+ */
+void
+active_gtt_shared_hash_init(void)
+{
+	HASHCTL info;
+	bool	found;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	gtt_shared_ctl =
+		ShmemInitStruct("gtt_shared_ctl",
+						sizeof(gtt_ctl_data),
+						&found);
+
+	if (!found)
+	{
+		LWLockRegisterTranche(LWTRANCHE_GLOBAL_TEMP_TABLE_CTL, "gtt_shared_ctl");
+		LWLockInitialize(&gtt_shared_ctl->lock, LWTRANCHE_GLOBAL_TEMP_TABLE_CTL);
+		gtt_shared_ctl->max_entry = max_active_gtt;
+		gtt_shared_ctl->entry_size = action_gtt_shared_hash_entry_size();
+	}
+
+	MemSet(&info, 0, sizeof(info));
+	info.keysize = sizeof(gtt_fnode);
+	info.entrysize = action_gtt_shared_hash_entry_size();
+	active_gtt_shared_hash =
+		ShmemInitHash("active gtt shared hash",
+						gtt_shared_ctl->max_entry,
+						gtt_shared_ctl->max_entry,
+						&info, HASH_ELEM | HASH_BLOBS | HASH_FIXED_SIZE);
+}
+
+/*
+ * Record GTT relid to shared hash table, which means that current session is using this GTT.
+ */
+static void
+gtt_storage_checkin(Oid relid)
+{
+	gtt_shared_hash_entry	*entry;
+	bool			found;
+	gtt_fnode		fnode;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	fnode.dbNode = MyDatabaseId;
+	fnode.relNode = relid;
+	LWLockAcquire(&gtt_shared_ctl->lock, LW_EXCLUSIVE);
+	entry = hash_search(active_gtt_shared_hash,
+						(void *)&(fnode), HASH_ENTER_NULL, &found);
+
+	if (entry == NULL)
+	{
+		LWLockRelease(&gtt_shared_ctl->lock);
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of shared memory"),
+				 errhint("You might need to increase max_active_global_temporary_table.")));
+	}
+
+	if (!found)
+	{
+		int			wordnum;
+
+		/* init bitmap */
+		entry->map = (Bitmapset *)((char *)entry + MAXALIGN(sizeof(gtt_shared_hash_entry)));
+		wordnum = WORDNUM(MaxBackends + 1);
+		memset(entry->map, 0, BITMAPSET_SIZE(wordnum + 1));
+		entry->map->nwords = wordnum + 1;
+	}
+
+	/* record itself in bitmap */
+	bms_add_member(entry->map, MyBackendId);
+	LWLockRelease(&gtt_shared_ctl->lock);
+}
+
+/*
+ * Remove the GTT relid record from the shared hash table which means that current session is
+ * not use this GTT.
+ */
+static void
+gtt_storage_checkout(Oid relid, bool isCommit)
+{
+	gtt_shared_hash_entry	*entry;
+	gtt_fnode		fnode;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	fnode.dbNode = MyDatabaseId;
+	fnode.relNode = relid;
+	LWLockAcquire(&gtt_shared_ctl->lock, LW_EXCLUSIVE);
+	entry = hash_search(active_gtt_shared_hash,
+					(void *) &(fnode), HASH_FIND, NULL);
+
+	if (entry == NULL)
+	{
+		LWLockRelease(&gtt_shared_ctl->lock);
+		if (isCommit)
+			elog(WARNING, "relid %u not exist in gtt shared hash when drop local storage", relid);
+
+		return;
+	}
+
+	Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
+
+	/* remove itself from bitmap */
+	bms_del_member(entry->map, MyBackendId);
+	if (bms_is_empty(entry->map))
+	{
+		if (!hash_search(active_gtt_shared_hash, &fnode, HASH_REMOVE, NULL))
+			elog(PANIC, "gtt shared hash table corrupted");
+	}
+	LWLockRelease(&gtt_shared_ctl->lock);
+
+	return;
+}
+
+/*
+ * Gets usage information for a GTT from shared hash table.
+ * The information is in the form of bitmap.
+ * Quickly copy the entire bitmap from shared memory and return it.
+ * that to avoid holding locks for a long time.
+ */
+static Bitmapset *
+copy_active_gtt_bitmap(Oid relid)
+{
+	gtt_shared_hash_entry	*entry;
+	Bitmapset		*map_copy = NULL;
+	gtt_fnode		fnode;
+
+	if (max_active_gtt <= 0)
+		return NULL;
+
+	fnode.dbNode = MyDatabaseId;
+	fnode.relNode = relid;
+	LWLockAcquire(&gtt_shared_ctl->lock, LW_SHARED);
+	entry = hash_search(active_gtt_shared_hash,
+					(void *) &(fnode), HASH_FIND, NULL);
+
+	if (entry)
+	{
+		Assert(entry->map);
+		/* copy the entire bitmap */
+		if (!bms_is_empty(entry->map))
+			map_copy = bms_copy(entry->map);
+	}
+
+	LWLockRelease(&gtt_shared_ctl->lock);
+
+	return map_copy;
+}
+
+/*
+ * Check if there are other sessions using this GTT besides the current session.
+ */
+bool
+is_other_backend_use_gtt(Oid relid)
+{
+	gtt_shared_hash_entry	*entry;
+	bool			in_use = false;
+	int			num_use = 0;
+	gtt_fnode		fnode;
+
+	if (max_active_gtt <= 0)
+		return false;
+
+	fnode.dbNode = MyDatabaseId;
+	fnode.relNode = relid;
+	LWLockAcquire(&gtt_shared_ctl->lock, LW_SHARED);
+	entry = hash_search(active_gtt_shared_hash,
+						(void *) &(fnode), HASH_FIND, NULL);
+
+	if (entry == NULL)
+	{
+		LWLockRelease(&gtt_shared_ctl->lock);
+		return false;
+	}
+
+	Assert(entry->map);
+	Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
+
+	/* how many backend are using this GTT */
+	num_use = bms_num_members(entry->map);
+	if (num_use == 0)
+		in_use = false;
+	else if (num_use == 1)
+	{
+		/* check if this is itself */
+		if(bms_is_member(MyBackendId, entry->map))
+			in_use = false;
+		else
+			in_use = true;
+	}
+	else
+		in_use = true;
+
+	LWLockRelease(&gtt_shared_ctl->lock);
+
+	return in_use;
+}
+
+/*
+ * Record GTT information to local hash.
+ * They include GTT storage info, transaction info and statistical info.
+ */
+void
+remember_gtt_storage_info(RelFileNode rnode, Relation rel)
+{
+	gtt_local_hash_entry		*entry;
+	MemoryContext			oldcontext;
+	gtt_relfilenode			*new_node = NULL;
+	Oid				relid = RelationGetRelid(rel);
+	int				natts = 0;
+
+	if (max_active_gtt <= 0)
+		ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("Global temporary table feature is disable"),
+			 errhint("You might need to increase max_active_global_temporary_table to enable this feature.")));
+
+	if (RecoveryInProgress())
+		elog(ERROR, "readonly mode not support access global temporary table");
+
+	if (rel->rd_rel->relkind == RELKIND_INDEX &&
+		rel->rd_index &&
+		(!rel->rd_index->indisvalid ||
+		 !rel->rd_index->indisready ||
+		 !rel->rd_index->indislive))
+		 elog(ERROR, "invalid gtt index %s not allow to create storage", RelationGetRelationName(rel));
+
+	/* First time through: initialize the hash table */
+	if (gtt_storage_local_hash == NULL)
+	{
+#define GTT_LOCAL_HASH_SIZE		1024
+		HASHCTL		ctl;
+
+		if (!CacheMemoryContext)
+			CreateCacheMemoryContext();
+
+		gtt_info_context =
+			AllocSetContextCreate(CacheMemoryContext,
+								"gtt info context",
+								ALLOCSET_DEFAULT_SIZES);
+
+		MemSet(&ctl, 0, sizeof(ctl));
+		ctl.keysize = sizeof(Oid);
+		ctl.entrysize = sizeof(gtt_local_hash_entry);
+		ctl.hcxt = gtt_info_context;
+		gtt_storage_local_hash =
+			hash_create("global temporary table info",
+						GTT_LOCAL_HASH_SIZE,
+						&ctl, HASH_ELEM | HASH_BLOBS);
+	}
+
+	Assert(CacheMemoryContext);
+	Assert(gtt_info_context);
+	oldcontext = MemoryContextSwitchTo(gtt_info_context);
+
+	entry = gtt_search_by_relid(relid, true);
+	if (!entry)
+	{
+		bool		found = false;
+
+		/* Look up or create an entry */
+		entry = hash_search(gtt_storage_local_hash,
+						(void *) &relid, HASH_ENTER, &found);
+
+		if (found)
+		{
+			MemoryContextSwitchTo(oldcontext);
+			elog(ERROR, "backend %d relid %u already exists in gtt local hash",
+						MyBackendId, relid);
+		}
+
+		entry->relfilenode_list = NIL;
+		entry->relkind = rel->rd_rel->relkind;
+		entry->on_commit_delete = false;
+
+		if (entry->relkind == RELKIND_RELATION)
+		{
+			/* record the on commit clause */
+			if (RELATION_GTT_ON_COMMIT_DELETE(rel))
+			{
+				entry->on_commit_delete = true;
+				register_on_commit_action(RelationGetRelid(rel), ONCOMMIT_DELETE_ROWS, true);
+			}
+		}
+
+		if (entry->relkind == RELKIND_RELATION ||
+			entry->relkind == RELKIND_SEQUENCE)
+		{
+			gtt_storage_checkin(relid);
+		}
+	}
+
+	/* record storage info relstat columnstats and transaction info to relfilenode list */
+	new_node = palloc0(sizeof(gtt_relfilenode));
+	new_node->relfilenode = rnode.relNode;
+	new_node->spcnode = rnode.spcNode;
+	new_node->relpages = 0;
+	new_node->reltuples = 0;
+	new_node->relallvisible = 0;
+	new_node->relfrozenxid = InvalidTransactionId;
+	new_node->relminmxid = InvalidMultiXactId;
+	new_node->natts = 0;
+	new_node->attnum = NULL;
+	new_node->att_stat_tups = NULL;
+	entry->relfilenode_list = lappend(entry->relfilenode_list, new_node);
+
+	/* init column stats structure */
+	natts = RelationGetNumberOfAttributes(rel);
+	new_node->attnum = palloc0(sizeof(int) * natts);
+	new_node->att_stat_tups = palloc0(sizeof(HeapTuple) * natts);
+	new_node->natts = natts;
+
+	/* remember transaction info */
+	if (RELKIND_HAS_TABLE_AM(entry->relkind))
+	{
+		new_node->relfrozenxid = RecentXmin;
+		new_node->relminmxid = GetOldestMultiXactId();
+
+		insert_gtt_relfrozenxid_to_ordered_list(new_node->relfrozenxid);
+		set_gtt_session_relfrozenxid();
+	}
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Registration callbacks are used to trigger cleanup during process exit */
+	if (!gtt_cleaner_exit_registered)
+	{
+		before_shmem_exit(gtt_storage_removeall, 0);
+		gtt_cleaner_exit_registered = true;
+	}
+
+	return;
+}
+
+/*
+ * Remove GTT information from local hash when transaction commit/rollback.
+ */
+void
+forget_gtt_storage_info(Oid relid, RelFileNode rnode, bool isCommit)
+{
+	gtt_local_hash_entry	*entry = NULL;
+	gtt_relfilenode		*d_rnode = NULL;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	entry = gtt_search_by_relid(relid, true);
+	if (entry == NULL)
+	{
+		if (isCommit)
+			elog(ERROR,"gtt rel %u not found in local hash", relid);
+
+		return;
+	}
+
+	d_rnode = gtt_search_relfilenode(entry, rnode.relNode, rnode.spcNode, true);
+	if (d_rnode == NULL)
+	{
+		if (isCommit)
+			elog(ERROR,"gtt relfilenode %u not found in rel %u", rnode.relNode, relid);
+		else
+		{
+			/* rollback transaction */
+			if (entry->relfilenode_list == NIL)
+			{
+				if (entry->relkind == RELKIND_RELATION ||
+					entry->relkind == RELKIND_SEQUENCE)
+					gtt_storage_checkout(relid, isCommit);
+
+				hash_search(gtt_storage_local_hash,
+						(void *) &(relid), HASH_REMOVE, NULL);
+			}
+
+			return;
+		}
+	}
+
+	/* Clean up transaction info from Local order list and MyProc */
+	if (entry->relkind == RELKIND_RELATION ||
+		entry->relkind == RELKIND_TOASTVALUE)
+	{
+		Assert(TransactionIdIsNormal(d_rnode->relfrozenxid) || !isCommit);
+
+		/* this is valid relfrozenxid */
+		if (TransactionIdIsValid(d_rnode->relfrozenxid))
+		{
+			remove_gtt_relfrozenxid_from_ordered_list(d_rnode->relfrozenxid);
+			set_gtt_session_relfrozenxid();
+		}
+	}
+
+	/* delete relfilenode from rel entry */
+	entry->relfilenode_list = list_delete_ptr(entry->relfilenode_list, d_rnode);
+	gtt_free_statistics(d_rnode);
+
+	if (entry->relfilenode_list == NIL)
+	{
+		/* tell shared hash that current session will no longer use this GTT */
+		if (entry->relkind == RELKIND_RELATION ||
+			entry->relkind == RELKIND_SEQUENCE)
+			gtt_storage_checkout(relid, isCommit);
+
+		hash_search(gtt_storage_local_hash,
+				(void *) &(relid), HASH_REMOVE, NULL);
+	}
+
+	return;
+}
+
+/*
+ * Check if current session is using this GTT.
+ */
+bool
+gtt_storage_attached(Oid relid)
+{
+	bool			found = false;
+	gtt_local_hash_entry	*entry = NULL;
+
+	if (max_active_gtt <= 0)
+		return false;
+
+	if (!OidIsValid(relid))
+		return false;
+
+	entry = gtt_search_by_relid(relid, true);
+	if (entry)
+		found = true;
+
+	return found;
+}
+
+/*
+ * When backend exit, bulk cleaning all GTT storage and local buffer of this backend.
+ */
+static void
+gtt_storage_removeall(int code, Datum arg)
+{
+	HASH_SEQ_STATUS			status;
+	gtt_local_hash_entry	*entry;
+
+	if (gtt_storage_local_hash == NULL)
+		return;
+
+	/* Need to ensure we have a usable transaction. */
+	AbortOutOfAnyTransaction();
+
+	/* Search all relfilenode for GTT in current session */
+	hash_seq_init(&status, gtt_storage_local_hash);
+	while ((entry = (gtt_local_hash_entry *) hash_seq_search(&status)) != NULL)
+	{
+		ListCell *lc;
+
+		foreach(lc, entry->relfilenode_list)
+		{
+			SMgrRelation	srel[1];
+			RelFileNode		rnode;
+			gtt_relfilenode *gtt_rnode = lfirst(lc);
+
+			rnode.spcNode = gtt_rnode->spcnode;
+			rnode.dbNode = MyDatabaseId;
+			rnode.relNode = gtt_rnode->relfilenode;
+			srel[0] = smgropen(rnode, MyBackendId);
+			smgrdounlinkall(srel, 1, false);
+			smgrclose(srel[0]);
+		}
+
+		if (entry->relkind == RELKIND_RELATION ||
+			entry->relkind == RELKIND_SEQUENCE)
+			gtt_storage_checkout(entry->relid, false);
+
+		hash_search(gtt_storage_local_hash, (void *) &(entry->relid), HASH_REMOVE, NULL);
+	}
+
+	/* set to global area */
+	MyProc->gtt_frozenxid = InvalidTransactionId;
+
+	return;
+}
+
+/*
+ * Update GTT relstats(relpage/reltuple/relallvisible)
+ * to local hash.
+ */
+void
+gtt_update_relstats(Relation relation, BlockNumber relpages, double reltuples,
+						BlockNumber relallvisible, TransactionId relfrozenxid,
+						TransactionId relminmxid)
+{
+	Oid						relid = RelationGetRelid(relation);
+	gtt_relfilenode			*gtt_rnode = NULL;
+	gtt_local_hash_entry	*entry = NULL;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	if (!OidIsValid(relid))
+		return;
+
+	entry = gtt_search_by_relid(relid, true);
+	if (entry == NULL)
+		return;
+
+	gtt_rnode = lfirst(list_tail(entry->relfilenode_list));
+	if (gtt_rnode == NULL)
+		return;
+
+	if (relpages > 0 &&
+		gtt_rnode->relpages != (int32)relpages)
+	{
+		gtt_rnode->relpages = (int32)relpages;
+		relation->rd_rel->relpages = (int32) relpages;
+	}
+
+	if (reltuples > 0 &&
+		gtt_rnode->reltuples != (float4)reltuples)
+	{
+		gtt_rnode->reltuples = (float4)reltuples;
+		relation->rd_rel->reltuples = (float4)reltuples;
+	}
+
+	/* only heap contain transaction information and relallvisible */
+	if (RELKIND_HAS_TABLE_AM(entry->relkind))
+	{
+		if (relallvisible > 0 &&
+			gtt_rnode->relallvisible != (int32)relallvisible)
+		{
+			gtt_rnode->relallvisible = (int32)relallvisible;
+			relation->rd_rel->relallvisible = (int32)relallvisible;
+		}
+
+		if (TransactionIdIsNormal(relfrozenxid) &&
+			gtt_rnode->relfrozenxid != relfrozenxid &&
+			(TransactionIdPrecedes(gtt_rnode->relfrozenxid, relfrozenxid) ||
+			 TransactionIdPrecedes(ReadNextTransactionId(), gtt_rnode->relfrozenxid)))
+		{
+			/* set to local order list */
+			remove_gtt_relfrozenxid_from_ordered_list(gtt_rnode->relfrozenxid);
+			gtt_rnode->relfrozenxid = relfrozenxid;
+			insert_gtt_relfrozenxid_to_ordered_list(relfrozenxid);
+			/* set to global area */
+			set_gtt_session_relfrozenxid();
+			relation->rd_rel->relfrozenxid = relfrozenxid;
+		}
+
+		if (MultiXactIdIsValid(relminmxid) &&
+			gtt_rnode->relminmxid != relminmxid &&
+			(MultiXactIdPrecedes(gtt_rnode->relminmxid, relminmxid) ||
+			 MultiXactIdPrecedes(ReadNextMultiXactId(), gtt_rnode->relminmxid)))
+		{
+			gtt_rnode->relminmxid = relminmxid;
+			relation->rd_rel->relminmxid = relminmxid;
+		}
+	}
+
+	return;
+}
+
+/*
+ * Search GTT relstats(relpage/reltuple/relallvisible)
+ * from local has.
+ */
+bool
+get_gtt_relstats(Oid relid, BlockNumber *relpages, double *reltuples,
+				BlockNumber *relallvisible, TransactionId *relfrozenxid,
+				TransactionId *relminmxid)
+{
+	gtt_local_hash_entry	*entry;
+	gtt_relfilenode		*gtt_rnode = NULL;
+
+	if (max_active_gtt <= 0)
+		return false;
+
+	entry = gtt_search_by_relid(relid, true);
+	if (entry == NULL)
+		return false;
+
+	Assert(entry->relid == relid);
+
+	gtt_rnode = lfirst(list_tail(entry->relfilenode_list));
+	if (gtt_rnode == NULL)
+		return false;
+
+	if (relpages)
+		*relpages = gtt_rnode->relpages;
+
+	if (reltuples)
+		*reltuples = gtt_rnode->reltuples;
+
+	if (relallvisible)
+		*relallvisible = gtt_rnode->relallvisible;
+
+	if (relfrozenxid)
+		*relfrozenxid = gtt_rnode->relfrozenxid;
+
+	if (relminmxid)
+		*relminmxid = gtt_rnode->relminmxid;
+
+	return true;
+}
+
+/*
+ * Update GTT info(definition is same as pg_statistic)
+ * to local hash.
+ */
+void
+up_gtt_att_statistic(Oid reloid, int attnum, bool inh, int natts,
+					TupleDesc tupleDescriptor, Datum *values, bool *isnull)
+{
+	gtt_local_hash_entry	*entry;
+	gtt_relfilenode		*gtt_rnode = NULL;
+	MemoryContext		oldcontext;
+	bool		found = false;
+	int			i = 0;
+
+	/* not support whole row or system column */
+	if (attnum <= 0)
+		return;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	entry = gtt_search_by_relid(reloid, true);
+	if (entry == NULL)
+		return;
+
+	Assert(entry->relid == reloid);
+
+	gtt_rnode = lfirst(list_tail(entry->relfilenode_list));
+	if (gtt_rnode == NULL)
+		return;
+
+	/* switch context to gtt_info_context for store tuple at heap_form_tuple */
+	oldcontext = MemoryContextSwitchTo(gtt_info_context);
+	for (i = 0; i < gtt_rnode->natts; i++)
+	{
+		if (gtt_rnode->attnum[i] == 0)
+		{
+			Assert(gtt_rnode->att_stat_tups[i] == NULL);
+			gtt_rnode->attnum[i] = attnum;
+			gtt_rnode->att_stat_tups[i] = heap_form_tuple(tupleDescriptor, values, isnull);
+			found = true;
+			break;
+		}
+		else if (gtt_rnode->attnum[i] == attnum)
+		{
+			Assert(gtt_rnode->att_stat_tups[i]);
+			heap_freetuple(gtt_rnode->att_stat_tups[i]);
+			gtt_rnode->att_stat_tups[i] = heap_form_tuple(tupleDescriptor, values, isnull);
+			found = true;
+			break;
+		}
+	}
+	MemoryContextSwitchTo(oldcontext);
+
+	if (!found)
+		elog(WARNING, "analyze can not update relid %u column %d statistics after add or drop column, try truncate table first", reloid, attnum);
+
+	return;
+}
+
+/*
+ * Search GTT statistic info(definition is same as pg_statistic)
+ * from local hash.
+ */
+HeapTuple
+get_gtt_att_statistic(Oid reloid, int attnum, bool inh)
+{
+	gtt_local_hash_entry	*entry;
+	int			i = 0;
+	gtt_relfilenode		*gtt_rnode = NULL;
+
+	/* not support whole row or system column */
+	if (attnum <= 0)
+		return NULL;
+
+	if (max_active_gtt <= 0)
+		return NULL;
+
+	entry = gtt_search_by_relid(reloid, true);
+	if (entry == NULL)
+		return NULL;
+
+	gtt_rnode = lfirst(list_tail(entry->relfilenode_list));
+	if (gtt_rnode == NULL)
+		return NULL;
+
+	for (i = 0; i < gtt_rnode->natts; i++)
+	{
+		if (gtt_rnode->attnum[i] == attnum)
+		{
+			Assert(gtt_rnode->att_stat_tups[i]);
+			return gtt_rnode->att_stat_tups[i];
+		}
+	}
+
+	return NULL;
+}
+
+void
+release_gtt_statistic_cache(HeapTuple tup)
+{
+	/* do nothing */
+	return;
+}
+
+/*
+ * Maintain a order relfrozenxid list of backend Level for GTT.
+ * Insert a RelfrozenXID into the list and keep the list in order.
+ */
+static void
+insert_gtt_relfrozenxid_to_ordered_list(Oid relfrozenxid)
+{
+	MemoryContext	oldcontext;
+	ListCell	*cell;
+	int		i;
+
+	Assert(TransactionIdIsNormal(relfrozenxid));
+
+	oldcontext = MemoryContextSwitchTo(gtt_info_context);
+
+	/* Does the datum belong at the front? */
+	if (gtt_session_relfrozenxid_list == NIL ||
+		TransactionIdFollowsOrEquals(relfrozenxid,
+			linitial_oid(gtt_session_relfrozenxid_list)))
+	{
+		gtt_session_relfrozenxid_list =
+			lcons_oid(relfrozenxid, gtt_session_relfrozenxid_list);
+		MemoryContextSwitchTo(oldcontext);
+
+		return;
+	}
+
+	/* No, so find the entry it belongs after */
+	i = 0;
+	foreach (cell, gtt_session_relfrozenxid_list)
+	{
+		if (TransactionIdFollowsOrEquals(relfrozenxid, lfirst_oid(cell)))
+			break;
+
+		i++;
+	}
+	gtt_session_relfrozenxid_list =
+		list_insert_nth_oid(gtt_session_relfrozenxid_list, i, relfrozenxid);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return;
+}
+
+/*
+ * Maintain a order relfrozenxid list of backend Level for GTT.
+ * Remove a RelfrozenXID from order list gtt_session_relfrozenxid_list.
+ */
+static void
+remove_gtt_relfrozenxid_from_ordered_list(Oid relfrozenxid)
+{
+	gtt_session_relfrozenxid_list =
+		list_delete_oid(gtt_session_relfrozenxid_list, relfrozenxid);
+}
+
+/*
+ * Update of backend Level oldest relfrozenxid to MyProc.
+ * This makes each backend's oldest RelFrozenxID globally visible.
+ */
+static void
+set_gtt_session_relfrozenxid(void)
+{
+	TransactionId gtt_frozenxid = InvalidTransactionId;
+
+	if (gtt_session_relfrozenxid_list != NIL)
+		gtt_frozenxid = llast_oid(gtt_session_relfrozenxid_list);
+
+	if (MyProc->gtt_frozenxid != gtt_frozenxid)
+		MyProc->gtt_frozenxid = gtt_frozenxid;
+}
+
+/*
+ * Get GTT column level data statistics.
+ */
+Datum
+pg_get_gtt_statistics(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo		*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Tuplestorestate		*tupstore;
+	HeapTuple		tuple;
+	Relation		rel = NULL;
+	Oid			reloid = PG_GETARG_OID(0);
+	int			attnum = PG_GETARG_INT32(1);
+	TupleDesc		tupdesc;
+	MemoryContext		oldcontext;
+	Relation		pg_tatistic = NULL;
+	TupleDesc		sd;
+
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			errmsg("set-valued function called in context that cannot accept a set")));
+
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			errmsg("materialize mode required, but it is not " \
+				"allowed in this context")));
+
+	oldcontext = MemoryContextSwitchTo(
+				rsinfo->econtext->ecxt_per_query_memory);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
+
+	rel = relation_open(reloid, AccessShareLock);
+	if (!RELATION_IS_GLOBAL_TEMP(rel))
+	{
+		elog(WARNING, "relation OID %u is not a global temporary table", reloid);
+		relation_close(rel, NoLock);
+		return (Datum) 0;
+	}
+
+	pg_tatistic = relation_open(StatisticRelationId, AccessShareLock);
+	sd = RelationGetDescr(pg_tatistic);
+
+	/* get data from local hash */
+	tuple = get_gtt_att_statistic(reloid, attnum, false);
+	if (tuple)
+	{
+		Datum		values[Natts_pg_statistic];
+		bool		isnull[Natts_pg_statistic];
+		HeapTuple	res = NULL;
+
+		memset(&values, 0, sizeof(values));
+		memset(&isnull, 0, sizeof(isnull));
+		heap_deform_tuple(tuple, sd, values, isnull);
+		res = heap_form_tuple(tupdesc, values, isnull);
+		tuplestore_puttuple(tupstore, res);
+	}
+	tuplestore_donestoring(tupstore);
+
+	relation_close(rel, AccessShareLock);
+	relation_close(pg_tatistic, AccessShareLock);
+
+	return (Datum) 0;
+}
+
+/*
+ * Get GTT table level data statistics.
+ */
+Datum
+pg_get_gtt_relstats(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo	*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Tuplestorestate	*tupstore;
+	TupleDesc	tupdesc;
+	MemoryContext	oldcontext;
+	HeapTuple	tuple;
+	Oid			reloid = PG_GETARG_OID(0);
+	Oid			relnode = 0;
+	BlockNumber		relpages = 0;
+	BlockNumber		relallvisible = 0;
+	uint32			relfrozenxid = 0;
+	uint32			relminmxid = 0;
+	double			reltuples = 0;
+	Relation		rel = NULL;
+
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not allowed in this context")));
+
+	oldcontext = MemoryContextSwitchTo(
+			rsinfo->econtext->ecxt_per_query_memory);
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
+
+	rel = relation_open(reloid, AccessShareLock);
+	if (!RELATION_IS_GLOBAL_TEMP(rel))
+	{
+		elog(WARNING, "relation OID %u is not a global temporary table", reloid);
+		relation_close(rel, NoLock);
+		return (Datum) 0;
+	}
+
+	get_gtt_relstats(reloid,
+					&relpages, &reltuples, &relallvisible,
+					&relfrozenxid, &relminmxid);
+	relnode = gtt_fetch_current_relfilenode(reloid);
+	if (relnode != InvalidOid)
+	{
+		Datum	values[6];
+		bool	isnull[6];
+
+		memset(isnull, 0, sizeof(isnull));
+		memset(values, 0, sizeof(values));
+		values[0] = UInt32GetDatum(relnode);
+		values[1] = Int32GetDatum(relpages);
+		values[2] = Float4GetDatum((float4)reltuples);
+		values[3] = Int32GetDatum(relallvisible);
+		values[4] = UInt32GetDatum(relfrozenxid);
+		values[5] = UInt32GetDatum(relminmxid);
+		tuple = heap_form_tuple(tupdesc, values, isnull);
+		tuplestore_puttuple(tupstore, tuple);
+	}
+	tuplestore_donestoring(tupstore);
+
+	relation_close(rel, NoLock);
+
+	return (Datum) 0;
+}
+
+/*
+ * Get a list of backend pids that are currently using this GTT.
+ */
+Datum
+pg_gtt_attached_pid(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo		*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	PGPROC			*proc = NULL;
+	Bitmapset		*map = NULL;
+	Tuplestorestate		*tupstore;
+	TupleDesc		tupdesc;
+	MemoryContext		oldcontext;
+	HeapTuple		tuple;
+	Oid			reloid = PG_GETARG_OID(0);
+	Relation		rel = NULL;
+	int				backendid = 0;
+
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not allowed in this context")));
+
+	oldcontext = MemoryContextSwitchTo(
+			rsinfo->econtext->ecxt_per_query_memory);
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
+
+	rel = relation_open(reloid, AccessShareLock);
+	if (!RELATION_IS_GLOBAL_TEMP(rel))
+	{
+		elog(WARNING, "relation OID %u is not a global temporary table", reloid);
+		relation_close(rel, NoLock);
+		return (Datum) 0;
+	}
+
+	/* get data from share hash */
+	map = copy_active_gtt_bitmap(reloid);
+	if (map)
+	{
+		backendid = bms_first_member(map);
+
+		do
+		{
+			/* backendid map to process pid */
+			proc = BackendIdGetProc(backendid);
+			if (proc && proc->pid > 0)
+			{
+				Datum	values[2];
+				bool	isnull[2];
+				pid_t	pid = proc->pid;
+
+				memset(isnull, 0, sizeof(isnull));
+				memset(values, 0, sizeof(values));
+				values[0] = UInt32GetDatum(reloid);
+				values[1] = Int32GetDatum(pid);
+				tuple = heap_form_tuple(tupdesc, values, isnull);
+				tuplestore_puttuple(tupstore, tuple);
+			}
+			backendid = bms_next_member(map, backendid);
+		} while (backendid > 0);
+
+		pfree(map);
+	}
+
+	tuplestore_donestoring(tupstore);
+	relation_close(rel, AccessShareLock);
+
+	return (Datum) 0;
+}
+
+/*
+ * Get backend level oldest relfrozenxid of each backend using GTT in current database.
+ */
+Datum
+pg_list_gtt_relfrozenxids(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo		*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Tuplestorestate		*tupstore;
+	TupleDesc		tupdesc;
+	MemoryContext	oldcontext;
+	TransactionId	oldest = InvalidTransactionId;
+	List			*pids = NULL;
+	List			*xids = NULL;
+
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not allowed in this context")));
+
+	oldcontext = MemoryContextSwitchTo(
+			rsinfo->econtext->ecxt_per_query_memory);
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
+
+	if (max_active_gtt <= 0)
+		return (Datum) 0;
+
+	if (RecoveryInProgress())
+		return (Datum) 0;
+
+	/* Get all session level oldest relfrozenxid that in current database use global temp table */
+	oldest = gtt_get_oldest_frozenxids_in_current_database(&pids, &xids);
+	if (TransactionIdIsValid(oldest))
+	{
+		HeapTuple		tuple;
+		ListCell		*lc1 = NULL;
+		ListCell		*lc2 = NULL;
+
+		/* Save db level oldest relfrozenxid */
+		pids = lappend_int(pids, 0);
+		xids = lappend_oid(xids, oldest);
+
+		Assert(list_length(pids) == list_length(xids));
+		forboth(lc1, pids, lc2, xids)
+		{
+			Datum	values[2];
+			bool	isnull[2];
+
+			memset(isnull, 0, sizeof(isnull));
+			memset(values, 0, sizeof(values));
+			values[0] = Int32GetDatum(lfirst_int(lc1));
+			values[1] = UInt32GetDatum(lfirst_oid(lc2));
+			tuple = heap_form_tuple(tupdesc, values, isnull);
+			tuplestore_puttuple(tupstore, tuple);
+		}
+	}
+	tuplestore_donestoring(tupstore);
+	list_free(pids);
+	list_free(xids);
+
+	return (Datum) 0;
+}
+
+/*
+ * In order to build the GTT index, force enable GTT'index.
+ */
+void
+force_enable_gtt_index(Relation index)
+{
+	if (!RELATION_IS_GLOBAL_TEMP(index))
+		return;
+
+	Assert(index->rd_rel->relkind == RELKIND_INDEX);
+	Assert(OidIsValid(RelationGetRelid(index)));
+
+	index->rd_index->indisvalid = true;
+	index->rd_index->indislive = true;
+	index->rd_index->indisready = true;
+}
+
+/*
+ * Fix the local state of the GTT's index.
+ */
+void
+gtt_correct_index_session_state(Relation index)
+{
+	Oid indexOid = RelationGetRelid(index);
+	Oid heapOid = index->rd_index->indrelid;
+
+	/* Must be GTT */
+	if (!RELATION_IS_GLOBAL_TEMP(index))
+		return;
+
+	if (!index->rd_index->indisvalid)
+		return;
+
+	/*
+	 * If this GTT is not initialized in the current session,
+	 * its index status is temporarily set to invalid(local relcache).
+	 */
+	if (gtt_storage_attached(heapOid) &&
+		!gtt_storage_attached(indexOid))
+	{
+		index->rd_index->indisvalid = false;
+		index->rd_index->indislive = false;
+		index->rd_index->indisready = false;
+	}
+
+	return;
+}
+
+/*
+ * Initialize storage of GTT and build empty index in this session.
+ */
+void
+gtt_init_storage(CmdType operation, Relation relation)
+{
+	Oid			toastrelid;
+	List		*indexoidlist = NIL;
+	ListCell	*l;
+
+	if (!(operation == CMD_INSERT))
+		return;
+
+	if (!RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
+		return;
+
+	if (!RELATION_IS_GLOBAL_TEMP(relation))
+		return;
+
+	/* Each GTT is initialized once in each backend */
+	if (gtt_storage_attached(RelationGetRelid(relation)))
+		return;
+
+	/* init heap storage */
+	RelationCreateStorage(relation->rd_node, RELPERSISTENCE_GLOBAL_TEMP, relation);
+
+	indexoidlist = RelationGetIndexList(relation);
+	foreach(l, indexoidlist)
+	{
+		Oid			indexOid = lfirst_oid(l);
+		Relation	index = index_open(indexOid, RowExclusiveLock);
+		IndexInfo	*info = BuildDummyIndexInfo(index);
+
+		index_build(relation, index, info, true, false);
+		/* after build index, index re-enabled */
+		Assert(index->rd_index->indisvalid);
+		Assert(index->rd_index->indislive);
+		Assert(index->rd_index->indisready);
+		index_close(index, NoLock);
+	}
+	list_free(indexoidlist);
+
+	/* rebuild index for global temp toast table */
+	toastrelid = relation->rd_rel->reltoastrelid;
+	if (OidIsValid(toastrelid))
+	{
+		Relation	toastrel;
+		ListCell	*indlist;
+
+		toastrel = table_open(toastrelid, RowExclusiveLock);
+
+		/* init index storage */
+		RelationCreateStorage(toastrel->rd_node, RELPERSISTENCE_GLOBAL_TEMP, toastrel);
+
+		foreach(indlist, RelationGetIndexList(toastrel))
+		{
+			Oid			indexId = lfirst_oid(indlist);
+			Relation	index = index_open(indexId, RowExclusiveLock);
+			IndexInfo	*info = BuildDummyIndexInfo(index);
+
+			/* build empty index */
+			index_build(toastrel, index, info, true, false);
+			Assert(index->rd_index->indisvalid);
+			Assert(index->rd_index->indislive);
+			Assert(index->rd_index->indisready);
+			index_close(index, NoLock);
+		}
+
+		table_close(toastrel, NoLock);
+	}
+
+	return;
+}
+
+/*
+ * Release the data structure memory used to store GTT storage info.
+ */
+static void
+gtt_free_statistics(gtt_relfilenode *rnode)
+{
+	int i;
+
+	Assert(rnode);
+
+	for (i = 0; i < rnode->natts; i++)
+	{
+		if (rnode->att_stat_tups[i])
+		{
+			heap_freetuple(rnode->att_stat_tups[i]);
+			rnode->att_stat_tups[i] = NULL;
+		}
+	}
+
+	if (rnode->attnum)
+		pfree(rnode->attnum);
+
+	if (rnode->att_stat_tups)
+		pfree(rnode->att_stat_tups);
+
+	pfree(rnode);
+
+	return;
+}
+
+/*
+ * Get the current relfilenode of this GTT.
+ */
+Oid
+gtt_fetch_current_relfilenode(Oid relid)
+{
+	gtt_local_hash_entry	*entry;
+	gtt_relfilenode			*gtt_rnode = NULL;
+
+	if (max_active_gtt <= 0)
+		return InvalidOid;
+
+	entry = gtt_search_by_relid(relid, true);
+	if (entry == NULL)
+		return InvalidOid;
+
+	Assert(entry->relid == relid);
+	gtt_rnode = lfirst(list_tail(entry->relfilenode_list));
+	if (gtt_rnode == NULL)
+		return InvalidOid;
+
+	return gtt_rnode->relfilenode;
+}
+
+/*
+ * Get a relfilenode used by this GTT during the transaction life cycle.
+ */
+static gtt_relfilenode *
+gtt_search_relfilenode(gtt_local_hash_entry	*entry, Oid relfilenode, Oid spcnode, bool missing_ok)
+{
+	gtt_relfilenode		*rnode = NULL;
+	ListCell		*lc;
+
+	Assert(entry);
+
+	foreach(lc, entry->relfilenode_list)
+	{
+		gtt_relfilenode	*gtt_rnode = lfirst(lc);
+		if (gtt_rnode->relfilenode == relfilenode &&
+			gtt_rnode->spcnode == spcnode)
+		{
+			rnode = gtt_rnode;
+			break;
+		}
+	}
+
+	if (!missing_ok && rnode == NULL)
+		elog(ERROR, "find relfilenode %u relfilenodelist from relid %u fail", relfilenode, entry->relid);
+
+	return rnode;
+}
+
+/*
+ * Get one GTT info from local hash.
+ */
+static gtt_local_hash_entry *
+gtt_search_by_relid(Oid relid, bool missing_ok)
+{
+	gtt_local_hash_entry	*entry = NULL;
+
+	if (gtt_storage_local_hash == NULL)
+		return NULL;
+
+	entry = hash_search(gtt_storage_local_hash,
+				(void *) &(relid), HASH_FIND, NULL);
+
+	if (entry == NULL && !missing_ok)
+		elog(ERROR, "relid %u not found in local hash", relid);
+
+	return entry;
+}
+
+/*
+ * update pg_class entry after CREATE INDEX or REINDEX for global temp table
+ */
+void
+index_update_gtt_relstats(Relation rel, bool hasindex, double reltuples, bool isreindex)
+{
+	Oid			relid = RelationGetRelid(rel);
+
+	Assert(RELATION_IS_GLOBAL_TEMP(rel));
+
+	/* see index_update_stats() */
+	if (reltuples == 0 && rel->rd_rel->reltuples < 0)
+		reltuples = -1;
+
+	/* update reltuples relpages relallvisible to localhash */
+	if (reltuples >= 0)
+	{
+		BlockNumber relpages = RelationGetNumberOfBlocks(rel);
+		BlockNumber relallvisible = 0;
+
+		if (rel->rd_rel->relkind != RELKIND_INDEX)
+			visibilitymap_count(rel, &relallvisible, NULL);
+		else
+			relallvisible = 0;
+
+		gtt_update_relstats(rel, relpages, reltuples, relallvisible,
+						InvalidTransactionId, InvalidMultiXactId);
+	}
+
+	/* update relhasindex to pg_class */
+	if (hasindex != rel->rd_rel->relhasindex)
+	{
+		Relation		pg_class = table_open(RelationRelationId, RowExclusiveLock);
+		Form_pg_class	rd_rel;
+		HeapTuple		tuple;
+
+		Assert(rel->rd_rel->relkind != RELKIND_INDEX);
+		tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "could not find tuple for relation %u", relid);
+
+		rd_rel = (Form_pg_class) GETSTRUCT(tuple);
+		rd_rel->relhasindex = hasindex;
+		heap_inplace_update(pg_class, tuple);
+		heap_freetuple(tuple);
+		table_close(pg_class, RowExclusiveLock);
+	}
+	else if (!isreindex)
+	{
+		/*
+		 * For global temp table
+		 * Even if pg_class does not change, relcache needs to be rebuilt
+		 * for flush rd_indexlist list (for a table) at create index process.
+		 *
+		 * Each session index has an independent data and cache(rd_amcache)
+		 * so relcache of the table and index do not need to be refreshed at reindex process.
+		 * This is different from the reindex of a regular table.
+		 */
+		CacheInvalidateRelcache(rel);
+	}
+}
+
+/*
+ * update statistics for one global temp relation
+ */
+void
+vac_update_gtt_relstats(Relation relation,
+					BlockNumber num_pages, double num_tuples,
+					BlockNumber num_all_visible_pages,
+					bool hasindex, TransactionId frozenxid,
+					MultiXactId minmulti, bool in_outer_xact)
+{
+	Oid			relid = RelationGetRelid(relation);
+	Relation	pg_class;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+	bool		dirty = false;
+	List		*idxs = NIL;
+
+	Assert(RELATION_IS_GLOBAL_TEMP(relation));
+
+	/* For global temporary table, store relstats and transaction info to the localhash */
+	gtt_update_relstats(relation, num_pages, num_tuples,
+						num_all_visible_pages, frozenxid, minmulti);
+
+	if (relation->rd_rel->relkind == RELKIND_RELATION)
+		idxs = RelationGetIndexList(relation);
+
+	pg_class = table_open(RelationRelationId, RowExclusiveLock);
+
+	/* Fetch a copy of the tuple to scribble on */
+	ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
+			 relid);
+	pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+	/* Apply DDL updates, but not inside an outer transaction (see above) */
+	if (!in_outer_xact)
+	{
+		/*
+		 * If we didn't find any indexes, reset relhasindex.
+		 *
+		 * Global temporary table may contain indexes that are not valid locally.
+		 * The catalog should not be updated based on local invalid index.
+		 */
+		if (pgcform->relhasindex && !hasindex && idxs == NIL)
+		{
+			pgcform->relhasindex = false;
+			dirty = true;
+		}
+
+		/* We also clear relhasrules and relhastriggers if needed */
+		if (pgcform->relhasrules && relation->rd_rules == NULL)
+		{
+			pgcform->relhasrules = false;
+			dirty = true;
+		}
+		if (pgcform->relhastriggers && relation->trigdesc == NULL)
+		{
+			pgcform->relhastriggers = false;
+			dirty = true;
+		}
+	}
+
+	/* If anything changed, write out the tuple. */
+	if (dirty)
+		heap_inplace_update(pg_class, ctup);
+
+	table_close(pg_class, RowExclusiveLock);
+
+	list_free(idxs);
+}
+
+void
+GlobalTempRelationSetNewRelfilenode(Relation relation)
+{
+	Oid			newrelfilenode;
+	MultiXactId minmulti = InvalidMultiXactId;
+	TransactionId freezeXid = InvalidTransactionId;
+	RelFileNode newrnode;
+
+	Assert(RELATION_IS_GLOBAL_TEMP(relation));
+	Assert(!RelationIsMapped(relation));
+
+	/* Allocate a new relfilenode */
+	newrelfilenode = GetNewRelFileNode(relation->rd_rel->reltablespace, NULL,
+									   RELPERSISTENCE_GLOBAL_TEMP);
+
+	/*
+	 * Schedule unlinking of the old storage at transaction commit.
+	 */
+	RelationDropStorage(relation);
+
+	newrnode = relation->rd_node;
+	newrnode.relNode = newrelfilenode;
+
+	if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
+	{
+		table_relation_set_new_filenode(relation, &newrnode,
+										RELPERSISTENCE_GLOBAL_TEMP,
+										&freezeXid, &minmulti);
+	}
+	else if (RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
+	{
+		/* handle these directly, at least for now */
+		SMgrRelation srel;
+
+		srel = RelationCreateStorage(newrnode, RELPERSISTENCE_GLOBAL_TEMP, relation);
+		smgrclose(srel);
+	}
+	else
+	{
+		/* we shouldn't be called for anything else */
+		elog(ERROR, "relation \"%s\" does not have storage",
+			 RelationGetRelationName(relation));
+	}
+
+	RelationAssumeNewRelfilenode(relation);
+
+	/* The local relcache and hashtable have been updated */
+	Assert(gtt_fetch_current_relfilenode(RelationGetRelid(relation)) == newrelfilenode);
+	Assert(relation->rd_node.relNode == newrelfilenode);
+}
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 3cb69b1f87b..1625ee9345b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -186,6 +186,91 @@ CREATE VIEW pg_sequences AS
     WHERE NOT pg_is_other_temp_schema(N.oid)
           AND relkind = 'S';
 
+-- For global temporary table
+CREATE VIEW pg_gtt_relstats WITH (security_barrier) AS
+ SELECT n.nspname AS schemaname,
+    c.relname AS tablename,
+    s.*
+ FROM
+     pg_class c
+     LEFT JOIN pg_namespace n ON n.oid = c.relnamespace,
+     pg_get_gtt_relstats(c.oid) as s
+ WHERE c.relpersistence='g' AND c.relkind in('r','p','i','t') AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));
+
+CREATE VIEW pg_gtt_attached_pids WITH (security_barrier) AS
+ SELECT n.nspname AS schemaname,
+    c.relname AS tablename,
+    s.*
+ FROM
+     pg_class c
+     LEFT JOIN pg_namespace n ON n.oid = c.relnamespace,
+     pg_gtt_attached_pid(c.oid) as s
+ WHERE c.relpersistence='g' AND c.relkind in('r','S') AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));
+
+CREATE VIEW pg_gtt_stats WITH (security_barrier) AS
+SELECT n.nspname AS schemaname,
+    c.relname AS tablename,
+    a.attname,
+    s.stainherit AS inherited,
+    s.stanullfrac AS null_frac,
+    s.stawidth AS avg_width,
+    s.stadistinct AS n_distinct,
+        CASE
+            WHEN s.stakind1 = 1 THEN s.stavalues1
+            WHEN s.stakind2 = 1 THEN s.stavalues2
+            WHEN s.stakind3 = 1 THEN s.stavalues3
+            WHEN s.stakind4 = 1 THEN s.stavalues4
+            WHEN s.stakind5 = 1 THEN s.stavalues5
+        END AS most_common_vals,
+        CASE
+            WHEN s.stakind1 = 1 THEN s.stanumbers1
+            WHEN s.stakind2 = 1 THEN s.stanumbers2
+            WHEN s.stakind3 = 1 THEN s.stanumbers3
+            WHEN s.stakind4 = 1 THEN s.stanumbers4
+            WHEN s.stakind5 = 1 THEN s.stanumbers5
+        END AS most_common_freqs,
+        CASE
+            WHEN s.stakind1 = 2 THEN s.stavalues1
+            WHEN s.stakind2 = 2 THEN s.stavalues2
+            WHEN s.stakind3 = 2 THEN s.stavalues3
+            WHEN s.stakind4 = 2 THEN s.stavalues4
+            WHEN s.stakind5 = 2 THEN s.stavalues5
+        END AS histogram_bounds,
+        CASE
+            WHEN s.stakind1 = 3 THEN s.stanumbers1[1]
+            WHEN s.stakind2 = 3 THEN s.stanumbers2[1]
+            WHEN s.stakind3 = 3 THEN s.stanumbers3[1]
+            WHEN s.stakind4 = 3 THEN s.stanumbers4[1]
+            WHEN s.stakind5 = 3 THEN s.stanumbers5[1]
+        END AS correlation,
+        CASE
+            WHEN s.stakind1 = 4 THEN s.stavalues1
+            WHEN s.stakind2 = 4 THEN s.stavalues2
+            WHEN s.stakind3 = 4 THEN s.stavalues3
+            WHEN s.stakind4 = 4 THEN s.stavalues4
+            WHEN s.stakind5 = 4 THEN s.stavalues5
+        END AS most_common_elems,
+        CASE
+            WHEN s.stakind1 = 4 THEN s.stanumbers1
+            WHEN s.stakind2 = 4 THEN s.stanumbers2
+            WHEN s.stakind3 = 4 THEN s.stanumbers3
+            WHEN s.stakind4 = 4 THEN s.stanumbers4
+            WHEN s.stakind5 = 4 THEN s.stanumbers5
+        END AS most_common_elem_freqs,
+        CASE
+            WHEN s.stakind1 = 5 THEN s.stanumbers1
+            WHEN s.stakind2 = 5 THEN s.stanumbers2
+            WHEN s.stakind3 = 5 THEN s.stanumbers3
+            WHEN s.stakind4 = 5 THEN s.stanumbers4
+            WHEN s.stakind5 = 5 THEN s.stanumbers5
+        END AS elem_count_histogram
+   FROM
+     pg_class c
+     JOIN pg_attribute a ON c.oid = a.attrelid
+     LEFT JOIN pg_namespace n ON n.oid = c.relnamespace,
+     pg_get_gtt_statistics(c.oid, a.attnum, ''::text) as s
+  WHERE c.relpersistence='g' AND c.relkind in('r','p','i','t') and NOT a.attisdropped AND has_column_privilege(c.oid, a.attnum, 'select'::text) AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));
+
 CREATE VIEW pg_stats WITH (security_barrier) AS
     SELECT
         nspname AS schemaname,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index a0da998c2ea..181bbb93f03 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -34,6 +34,7 @@
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_statistic_ext.h"
+#include "catalog/storage_gtt.h"
 #include "commands/dbcommands.h"
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
@@ -105,7 +106,7 @@ static int	acquire_inherited_sample_rows(Relation onerel, int elevel,
 										  HeapTuple *rows, int targrows,
 										  double *totalrows, double *totaldeadrows);
 static void update_attstats(Oid relid, bool inh,
-							int natts, VacAttrStats **vacattrstats);
+							int natts, VacAttrStats **vacattrstats, char relpersistence);
 static Datum std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
 static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
 
@@ -186,6 +187,17 @@ analyze_rel(Oid relid, RangeVar *relation,
 		return;
 	}
 
+	/*
+	 * Skip the global temporary table that did not initialize the storage
+	 * in this backend.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(onerel) &&
+		!gtt_storage_attached(RelationGetRelid(onerel)))
+	{
+		relation_close(onerel, ShareUpdateExclusiveLock);
+		return;
+	}
+
 	/*
 	 * We can ANALYZE any table except pg_statistic. See update_attstats
 	 */
@@ -602,14 +614,15 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
 		 * pg_statistic for columns we didn't process, we leave them alone.)
 		 */
 		update_attstats(RelationGetRelid(onerel), inh,
-						attr_cnt, vacattrstats);
+						attr_cnt, vacattrstats, RelationGetRelPersistence(onerel));
 
 		for (ind = 0; ind < nindexes; ind++)
 		{
 			AnlIndexData *thisdata = &indexdata[ind];
 
 			update_attstats(RelationGetRelid(Irel[ind]), false,
-							thisdata->attr_cnt, thisdata->vacattrstats);
+							thisdata->attr_cnt, thisdata->vacattrstats,
+							RelationGetRelPersistence(Irel[ind]));
 		}
 
 		/* Build extended statistics (if there are any). */
@@ -1617,7 +1630,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
  *		by taking a self-exclusive lock on the relation in analyze_rel().
  */
 static void
-update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats)
+update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats, char relpersistence)
 {
 	Relation	sd;
 	int			attno;
@@ -1719,31 +1732,48 @@ update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats)
 			}
 		}
 
-		/* Is there already a pg_statistic tuple for this attribute? */
-		oldtup = SearchSysCache3(STATRELATTINH,
-								 ObjectIdGetDatum(relid),
-								 Int16GetDatum(stats->attr->attnum),
-								 BoolGetDatum(inh));
-
-		if (HeapTupleIsValid(oldtup))
+		/*
+		 * For global temporary table,
+		 * Update column statistic to localhash, not pg_statistic.
+		 */
+		if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 		{
-			/* Yes, replace it */
-			stup = heap_modify_tuple(oldtup,
-									 RelationGetDescr(sd),
-									 values,
-									 nulls,
-									 replaces);
-			ReleaseSysCache(oldtup);
-			CatalogTupleUpdate(sd, &stup->t_self, stup);
+			up_gtt_att_statistic(relid,
+								stats->attr->attnum,
+								inh,
+								natts,
+								RelationGetDescr(sd),
+								values,
+								nulls);
 		}
 		else
 		{
-			/* No, insert new tuple */
-			stup = heap_form_tuple(RelationGetDescr(sd), values, nulls);
-			CatalogTupleInsert(sd, stup);
-		}
+			/* Is there already a pg_statistic tuple for this attribute? */
+			oldtup = SearchSysCache3(STATRELATTINH,
+									 ObjectIdGetDatum(relid),
+									 Int16GetDatum(stats->attr->attnum),
+									 BoolGetDatum(inh));
 
-		heap_freetuple(stup);
+			if (HeapTupleIsValid(oldtup))
+			{
+				/* Yes, replace it */
+				stup = heap_modify_tuple(oldtup,
+										 RelationGetDescr(sd),
+										 values,
+										 nulls,
+										 replaces);
+				ReleaseSysCache(oldtup);
+				CatalogTupleUpdate(sd, &stup->t_self, stup);
+			}
+			else
+			{
+				/* No, insert new tuple */
+				stup = heap_form_tuple(RelationGetDescr(sd), values, nulls);
+				CatalogTupleInsert(sd, stup);
+			}
+
+			heap_freetuple(stup);
+		}
 	}
 
 	table_close(sd, RowExclusiveLock);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 61853e6dec4..e0b8d1d0db7 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -33,6 +33,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/storage_gtt.h"
 #include "catalog/toasting.h"
 #include "commands/cluster.h"
 #include "commands/defrem.h"
@@ -390,6 +391,22 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
 					 errmsg("cannot vacuum temporary tables of other sessions")));
 	}
 
+	/*
+	 * Skip the global temporary table that did not initialize the storage
+	 * in this backend.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(OldHeap))
+	{
+		if (gtt_storage_attached(RelationGetRelid(OldHeap)))
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot cluster global temporary table")));
+
+		relation_close(OldHeap, AccessExclusiveLock);
+		pgstat_progress_end_command();
+		return;
+	}
+
 	/*
 	 * Also check for active uses of the relation in the current transaction,
 	 * including open scans and pending AFTER trigger events.
@@ -585,6 +602,9 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
 	TransactionId frozenXid;
 	MultiXactId cutoffMulti;
 
+	/* not support cluster global temp table yet */
+	Assert(!RELATION_IS_GLOBAL_TEMP(OldHeap));
+
 	/* Mark the correct index as clustered */
 	if (OidIsValid(indexOid))
 		mark_index_clustered(OldHeap, indexOid, true);
@@ -1429,7 +1449,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
 	pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
 								 PROGRESS_CLUSTER_PHASE_REBUILD_INDEX);
 
-	reindex_relation(OIDOldHeap, reindex_flags, &reindex_params);
+	reindex_relation(OIDOldHeap, reindex_flags, &reindex_params, ShareLock);
 
 	/* Report that we are now doing clean up */
 	pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index bb9c21bc6b4..18cd1d8c282 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -289,7 +289,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
 		Assert(rel);
 
 		/* check read-only transaction and parallel mode */
-		if (XactReadOnly && !rel->rd_islocaltemp)
+		if (XactReadOnly && !RELATION_IS_TEMP_ON_CURRENT_SESSION(rel))
 			PreventCommandIfReadOnly("COPY FROM");
 
 		cstate = BeginCopyFrom(pstate, rel, whereClause,
diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c
index 7b3f5a84b82..03692b4a73a 100644
--- a/src/backend/commands/copyfrom.c
+++ b/src/backend/commands/copyfrom.c
@@ -30,6 +30,7 @@
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/namespace.h"
+#include "catalog/storage_gtt.h"
 #include "commands/copy.h"
 #include "commands/copyfrom_internal.h"
 #include "commands/progress.h"
@@ -652,7 +653,7 @@ CopyFrom(CopyFromState cstate)
 	 */
 	ExecInitRangeTable(estate, cstate->range_table);
 	resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo);
-	ExecInitResultRelation(estate, resultRelInfo, 1);
+	ExecInitResultRelation(estate, resultRelInfo, 1, CMD_INSERT);
 
 	/* Verify the named relation is a valid target for INSERT */
 	CheckValidResultRel(resultRelInfo, CMD_INSERT);
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9abbb6b5552..a944f244496 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -32,6 +32,7 @@
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/namespace.h"
+#include "catalog/storage_gtt.h"
 #include "catalog/toasting.h"
 #include "commands/createas.h"
 #include "commands/matview.h"
@@ -520,6 +521,12 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 	 */
 	intoRelationDesc = table_open(intoRelationAddr.objectId, AccessExclusiveLock);
 
+	/*
+	 * Try initializing the global Temp table storage file before writing data
+	 * to the table.
+	 */
+	gtt_init_storage(CMD_INSERT, intoRelationDesc);
+
 	/*
 	 * Make sure the constructed table does not have RLS enabled.
 	 *
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index e5cf1bde13f..8daaed03bbc 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -111,6 +111,7 @@ struct ReindexIndexCallbackState
 {
 	ReindexParams params;		/* options from statement */
 	Oid			locked_table_oid;	/* tracks previously locked table */
+	LOCKMODE	lockmode;
 };
 
 /*
@@ -570,7 +571,7 @@ DefineIndex(Oid relationId,
 	 * is more efficient.  Do this before any use of the concurrent option is
 	 * done.
 	 */
-	if (stmt->concurrent && get_rel_persistence(relationId) != RELPERSISTENCE_TEMP)
+	if (stmt->concurrent && !RelpersistenceTsTemp(get_rel_persistence(relationId)))
 		concurrent = true;
 	else
 		concurrent = false;
@@ -2581,24 +2582,46 @@ ReindexIndex(RangeVar *indexRelation, ReindexParams *params, bool isTopLevel)
 	 */
 	state.params = *params;
 	state.locked_table_oid = InvalidOid;
+	state.lockmode = AccessShareLock;
 	indOid = RangeVarGetRelidExtended(indexRelation,
-									  (params->options & REINDEXOPT_CONCURRENTLY) != 0 ?
-									  ShareUpdateExclusiveLock : AccessExclusiveLock,
+									  AccessShareLock,
 									  0,
 									  RangeVarCallbackForReindexIndex,
 									  &state);
 
 	/*
 	 * Obtain the current persistence and kind of the existing index.  We
-	 * already hold a lock on the index.
+	 * already hold a AccessShareLock on the index.
+	 * If this is not a global temp object, apply a larger lock.
 	 */
 	persistence = get_rel_persistence(indOid);
-	relkind = get_rel_relkind(indOid);
+	if (persistence != RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		LOCKMODE	table_lockmode;
+		LOCKMODE	index_lockmode;
+
+		if ((params->options & REINDEXOPT_CONCURRENTLY) != 0)
+		{
+			table_lockmode = ShareUpdateExclusiveLock;
+			index_lockmode = ShareUpdateExclusiveLock;
+		}
+		else
+		{
+			table_lockmode = ShareLock;
+			index_lockmode = AccessExclusiveLock;
+		}
 
+		/* lock heap first */
+		Assert(OidIsValid(state.locked_table_oid));
+		LockRelationOid(state.locked_table_oid, table_lockmode);
+		LockRelationOid(indOid, index_lockmode);
+	}
+
+	relkind = get_rel_relkind(indOid);
 	if (relkind == RELKIND_PARTITIONED_INDEX)
 		ReindexPartitions(indOid, params, isTopLevel);
 	else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
-			 persistence != RELPERSISTENCE_TEMP)
+			 !RelpersistenceTsTemp(persistence))
 		ReindexRelationConcurrently(indOid, params);
 	else
 	{
@@ -2620,15 +2643,7 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation,
 {
 	char		relkind;
 	struct ReindexIndexCallbackState *state = arg;
-	LOCKMODE	table_lockmode;
-
-	/*
-	 * Lock level here should match table lock in reindex_index() for
-	 * non-concurrent case and table locks used by index_concurrently_*() for
-	 * concurrent case.
-	 */
-	table_lockmode = (state->params.options & REINDEXOPT_CONCURRENTLY) != 0 ?
-		ShareUpdateExclusiveLock : ShareLock;
+	LOCKMODE	table_lockmode = state->lockmode;
 
 	/*
 	 * If we previously locked some other index's heap, and the name we're
@@ -2689,6 +2704,8 @@ ReindexTable(RangeVar *relation, ReindexParams *params, bool isTopLevel)
 {
 	Oid			heapOid;
 	bool		result;
+	char		relpersistence;
+	LOCKMODE	lockmode;
 
 	/*
 	 * The lock level used here should match reindex_relation().
@@ -2699,15 +2716,27 @@ ReindexTable(RangeVar *relation, ReindexParams *params, bool isTopLevel)
 	 * locks on our temporary table.
 	 */
 	heapOid = RangeVarGetRelidExtended(relation,
-									   (params->options & REINDEXOPT_CONCURRENTLY) != 0 ?
-									   ShareUpdateExclusiveLock : ShareLock,
+									   AccessShareLock,
 									   0,
 									   RangeVarCallbackOwnsTable, NULL);
 
+	relpersistence = get_rel_persistence(heapOid);
+	if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		lockmode = AccessShareLock;
+	else
+	{
+		if ((params->options & REINDEXOPT_CONCURRENTLY) != 0)
+			lockmode = ShareUpdateExclusiveLock;
+		else
+			lockmode = ShareLock;
+
+		LockRelationOid(heapOid, lockmode);
+	}
+
 	if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE)
 		ReindexPartitions(heapOid, params, isTopLevel);
 	else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
-			 get_rel_persistence(heapOid) != RELPERSISTENCE_TEMP)
+			 !RelpersistenceTsTemp(relpersistence))
 	{
 		result = ReindexRelationConcurrently(heapOid, params);
 
@@ -2724,7 +2753,8 @@ ReindexTable(RangeVar *relation, ReindexParams *params, bool isTopLevel)
 		result = reindex_relation(heapOid,
 								  REINDEX_REL_PROCESS_TOAST |
 								  REINDEX_REL_CHECK_CONSTRAINTS,
-								  &newparams);
+								  &newparams,
+								  lockmode);
 		if (!result)
 			ereport(NOTICE,
 					(errmsg("table \"%s\" has no indexes to reindex",
@@ -3119,7 +3149,7 @@ ReindexMultipleInternal(List *relids, ReindexParams *params)
 		Assert(!RELKIND_HAS_PARTITIONS(relkind));
 
 		if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
-			relpersistence != RELPERSISTENCE_TEMP)
+			!RelpersistenceTsTemp(relpersistence))
 		{
 			ReindexParams newparams = *params;
 
@@ -3141,13 +3171,20 @@ ReindexMultipleInternal(List *relids, ReindexParams *params)
 		{
 			bool		result;
 			ReindexParams newparams = *params;
+			LOCKMODE	lockmode;
+
+			if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+				lockmode = AccessShareLock;
+			else
+				lockmode = ShareLock;
 
 			newparams.options |=
 				REINDEXOPT_REPORT_PROGRESS | REINDEXOPT_MISSING_OK;
 			result = reindex_relation(relid,
 									  REINDEX_REL_PROCESS_TOAST |
 									  REINDEX_REL_CHECK_CONSTRAINTS,
-									  &newparams);
+									  &newparams,
+									  lockmode);
 
 			if (result && (params->options & REINDEXOPT_VERBOSE) != 0)
 				ereport(INFO,
diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c
index 4b3f79704f8..c544885f53e 100644
--- a/src/backend/commands/lockcmds.c
+++ b/src/backend/commands/lockcmds.c
@@ -51,12 +51,33 @@ LockTableCommand(LockStmt *lockstmt)
 		RangeVar   *rv = (RangeVar *) lfirst(p);
 		bool		recurse = rv->inh;
 		Oid			reloid;
+		LOCKMODE	lockmode = lockstmt->mode;
+		char		relpersistence;
 
-		reloid = RangeVarGetRelidExtended(rv, lockstmt->mode,
-										  lockstmt->nowait ? RVR_NOWAIT : 0,
+		reloid = RangeVarGetRelidExtended(rv, NoLock, 0,
 										  RangeVarCallbackForLockTable,
 										  (void *) &lockstmt->mode);
 
+		relpersistence = get_rel_persistence(reloid);
+		/* lock statement does not hold any lock on global temp table */
+		if (relpersistence != RELPERSISTENCE_GLOBAL_TEMP)
+		{
+			if (!lockstmt->nowait)
+				LockRelationOid(reloid, lockmode);
+			else if (!ConditionalLockRelationOid(reloid, lockmode))
+			{
+				/* try to throw error by name; relation could be deleted... */
+				char	   *relname = get_rel_name(reloid);
+
+				if (!relname)
+					return;		/* child concurrently dropped, just skip it */
+				ereport(ERROR,
+						(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+						 errmsg("could not obtain lock on relation \"%s\"",
+								relname)));
+			}
+		}
+
 		if (get_rel_relkind(reloid) == RELKIND_VIEW)
 			LockViewRecurse(reloid, lockstmt->mode, lockstmt->nowait, NIL);
 		else if (recurse)
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 27cb6307581..093f76f2950 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -25,11 +25,14 @@
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
 #include "catalog/dependency.h"
+#include "catalog/heap.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_sequence.h"
 #include "catalog/pg_type.h"
+#include "catalog/storage.h"
+#include "catalog/storage_gtt.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
 #include "commands/tablecmds.h"
@@ -108,6 +111,7 @@ static void init_params(ParseState *pstate, List *options, bool for_identity,
 						List **owned_by);
 static void do_setval(Oid relid, int64 next, bool iscalled);
 static void process_owned_by(Relation seqrel, List *owned_by, bool for_identity);
+int64 get_seqence_start_value(Oid seqid);
 
 
 /*
@@ -220,9 +224,16 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
 	rel = table_open(seqoid, AccessExclusiveLock);
 	tupDesc = RelationGetDescr(rel);
 
-	/* now initialize the sequence's data */
-	tuple = heap_form_tuple(tupDesc, value, null);
-	fill_seq_with_data(rel, tuple);
+	/*
+	 * For global temp sequence, the storage is not initialized
+	 * when it is created, but when it is used.
+	 */
+	if (!RELATION_IS_GLOBAL_TEMP(rel))
+	{
+		/* now initialize the sequence's data */
+		tuple = heap_form_tuple(tupDesc, value, null);
+		fill_seq_with_data(rel, tuple);
+	}
 
 	/* process OWNED BY if given */
 	if (owned_by)
@@ -275,8 +286,6 @@ ResetSequence(Oid seq_relid)
 	Buffer		buf;
 	HeapTupleData seqdatatuple;
 	HeapTuple	tuple;
-	HeapTuple	pgstuple;
-	Form_pg_sequence pgsform;
 	int64		startv;
 
 	/*
@@ -287,12 +296,7 @@ ResetSequence(Oid seq_relid)
 	init_sequence(seq_relid, &elm, &seq_rel);
 	(void) read_seq_tuple(seq_rel, &buf, &seqdatatuple);
 
-	pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(seq_relid));
-	if (!HeapTupleIsValid(pgstuple))
-		elog(ERROR, "cache lookup failed for sequence %u", seq_relid);
-	pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
-	startv = pgsform->seqstart;
-	ReleaseSysCache(pgstuple);
+	startv = get_seqence_start_value(seq_relid);
 
 	/*
 	 * Copy the existing sequence tuple.
@@ -451,6 +455,9 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
 
 	init_sequence(relid, &elm, &seqrel);
 
+	if (RELATION_IS_GLOBAL_TEMP(seqrel))
+		CheckGlobalTempTableNotInUse(seqrel, "ALTER GLOBAL TEMPORARY SEQUENCE");
+
 	rel = table_open(SequenceRelationId, RowExclusiveLock);
 	seqtuple = SearchSysCacheCopy1(SEQRELID,
 								   ObjectIdGetDatum(relid));
@@ -611,7 +618,7 @@ nextval_internal(Oid relid, bool check_permissions)
 						RelationGetRelationName(seqrel))));
 
 	/* read-only transactions may only modify temp sequences */
-	if (!seqrel->rd_islocaltemp)
+	if (!RELATION_IS_TEMP_ON_CURRENT_SESSION(seqrel))
 		PreventCommandIfReadOnly("nextval()");
 
 	/*
@@ -936,7 +943,7 @@ do_setval(Oid relid, int64 next, bool iscalled)
 	ReleaseSysCache(pgstuple);
 
 	/* read-only transactions may only modify temp sequences */
-	if (!seqrel->rd_islocaltemp)
+	if (!RELATION_IS_TEMP_ON_CURRENT_SESSION(seqrel))
 		PreventCommandIfReadOnly("setval()");
 
 	/*
@@ -1153,6 +1160,14 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	/* Return results */
 	*p_elm = elm;
 	*p_rel = seqrel;
+
+	/* Initializes the storage for sequence which the global temporary table belongs. */
+	if (RELATION_IS_GLOBAL_TEMP(seqrel) &&
+		!gtt_storage_attached(RelationGetRelid(seqrel)))
+	{
+		RelationCreateStorage(seqrel->rd_node, RELPERSISTENCE_GLOBAL_TEMP, seqrel);
+		gtt_init_sequence(seqrel);
+	}
 }
 
 
@@ -1927,3 +1942,57 @@ seq_mask(char *page, BlockNumber blkno)
 
 	mask_unused_space(page);
 }
+
+/*
+ * Get the startValue of the sequence from syscache.
+ */
+int64
+get_seqence_start_value(Oid seqid)
+{
+	HeapTuple	seqtuple;
+	Form_pg_sequence	seqform;
+	int64		start;
+
+	seqtuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(seqid));
+	if (!HeapTupleIsValid(seqtuple))
+		elog(ERROR, "cache lookup failed for sequence %u",
+			 seqid);
+
+	seqform = (Form_pg_sequence) GETSTRUCT(seqtuple);
+	start = seqform->seqstart;
+	ReleaseSysCache(seqtuple);
+
+	return start;
+}
+
+/*
+ * Initialize sequence which global temporary table belongs.
+ */
+void
+gtt_init_sequence(Relation rel)
+{
+	Datum		value[SEQ_COL_LASTCOL];
+	bool		null[SEQ_COL_LASTCOL];
+	HeapTuple	tuple;
+	int64		startv = get_seqence_start_value(RelationGetRelid(rel));
+
+	/*
+	 * last_value from pg_sequence.seqstart
+	 * log_cnt = 0
+	 * is_called = false
+	 */
+	value[SEQ_COL_LASTVAL - 1] = Int64GetDatumFast(startv); /* start sequence with 1 */
+	null[SEQ_COL_LASTVAL - 1] = false;
+
+	value[SEQ_COL_LOG - 1] = Int64GetDatum((int64)0);
+	null[SEQ_COL_LOG - 1] = false;
+
+	value[SEQ_COL_CALLED - 1] = BoolGetDatum(false);
+	null[SEQ_COL_CALLED - 1] = false;
+
+	tuple = heap_form_tuple(RelationGetDescr(rel), value, null);
+	fill_seq_with_data(rel, tuple);
+	heap_freetuple(tuple);
+
+	return;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1f0654c2f51..a558e121596 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -47,6 +47,7 @@
 #include "catalog/storage.h"
 #include "catalog/storage_xlog.h"
 #include "catalog/toasting.h"
+#include "catalog/storage_gtt.h"
 #include "commands/cluster.h"
 #include "commands/comment.h"
 #include "commands/defrem.h"
@@ -118,6 +119,7 @@ typedef struct OnCommitItem
 	 */
 	SubTransactionId creating_subid;
 	SubTransactionId deleting_subid;
+	bool			 is_global_temp;
 } OnCommitItem;
 
 static List *on_commits = NIL;
@@ -624,7 +626,7 @@ static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
 static List *GetParentedForeignKeyRefs(Relation partition);
 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
 static char GetAttributeCompression(Oid atttypid, char *compression);
-
+static OnCommitAction gtt_oncommit_option(List *options);
 
 /* ----------------------------------------------------------------
  *		DefineRelation
@@ -669,6 +671,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	LOCKMODE	parentLockmode;
 	const char *accessMethod = NULL;
 	Oid			accessMethodId = InvalidOid;
+	OnCommitAction	oncommit_action = ONCOMMIT_NOOP;
 
 	/*
 	 * Truncate relname to appropriate length (probably a waste of time, as
@@ -680,7 +683,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	 * Check consistency of arguments
 	 */
 	if (stmt->oncommit != ONCOMMIT_NOOP
-		&& stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
+		&& !RelpersistenceTsTemp(stmt->relation->relpersistence))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 				 errmsg("ON COMMIT can only be used on temporary tables")));
@@ -710,7 +713,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	 * code.  This is needed because calling code might not expect untrusted
 	 * tables to appear in pg_temp at the front of its search path.
 	 */
-	if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
+	if (RelpersistenceTsTemp(stmt->relation->relpersistence)
 		&& InSecurityRestrictedOperation())
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -811,6 +814,59 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
+	/* For global temporary table */
+	oncommit_action = gtt_oncommit_option(stmt->options);
+	if (stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		if (!(relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+					 errmsg("global temporary relation can only be a regular table or sequence")));
+
+		if (inheritOids)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+					 errmsg("cannot create global temporary inherit table or global temporary partitioned table")));
+
+		/* Check oncommit clause and save to reloptions */
+		if (oncommit_action != ONCOMMIT_NOOP)
+		{
+			if (stmt->oncommit != ONCOMMIT_NOOP)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("cannot specify both ON COMMIT clause and on_commit_delete_rows")));
+
+			stmt->oncommit = oncommit_action;
+		}
+		else
+		{
+			DefElem *opt = makeNode(DefElem);
+
+			opt->type = T_DefElem;
+			opt->defnamespace = NULL;
+			opt->defname = "on_commit_delete_rows";
+			opt->defaction = DEFELEM_UNSPEC;
+
+			/* use reloptions to remember on commit clause */
+			if (stmt->oncommit == ONCOMMIT_DELETE_ROWS)
+				opt->arg  = (Node *)makeString("true");
+			else if (stmt->oncommit == ONCOMMIT_PRESERVE_ROWS)
+				opt->arg  = (Node *)makeString("false");
+			else if (stmt->oncommit == ONCOMMIT_NOOP)
+				opt->arg  = (Node *)makeString("false");
+			else if (stmt->oncommit == ONCOMMIT_DROP)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("specifying ON COMMIT DROP is not supported on a global temporary table")));
+
+			stmt->options = lappend(stmt->options, opt);
+		}
+	}
+	else if (oncommit_action != ONCOMMIT_NOOP)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				 errmsg("on_commit_delete_rows can only be used on global temporary table")));
+
 	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
 									 true, false);
 
@@ -1435,7 +1491,7 @@ RemoveRelations(DropStmt *drop)
 		 * relation persistence cannot be known without its OID.
 		 */
 		if (drop->concurrent &&
-			get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
+			!RelpersistenceTsTemp(get_rel_persistence(relOid)))
 		{
 			Assert(list_length(drop->objects) == 1 &&
 				   drop->removeType == OBJECT_INDEX);
@@ -1644,9 +1700,9 @@ ExecuteTruncate(TruncateStmt *stmt)
 		Relation	rel;
 		bool		recurse = rv->inh;
 		Oid			myrelid;
-		LOCKMODE	lockmode = AccessExclusiveLock;
+		LOCKMODE	lockmode;
 
-		myrelid = RangeVarGetRelidExtended(rv, lockmode,
+		myrelid = RangeVarGetRelidExtended(rv, AccessShareLock,
 										   0, RangeVarCallbackForTruncate,
 										   NULL);
 
@@ -1654,9 +1710,21 @@ ExecuteTruncate(TruncateStmt *stmt)
 		if (list_member_oid(relids, myrelid))
 			continue;
 
-		/* open the relation, we already hold a lock on it */
+		/* open the relation, we need hold a low-level lock first */
 		rel = table_open(myrelid, NoLock);
 
+		/*
+		 * Truncate global temp table only cleans up the data in current session,
+		 * only low-level locks are required.
+		 */
+		if (RELATION_IS_GLOBAL_TEMP(rel))
+			lockmode = AccessShareLock;
+		else
+		{
+			lockmode = AccessExclusiveLock;
+			LockRelationOid(myrelid, lockmode);
+		}
+
 		/*
 		 * RangeVarGetRelidExtended() has done most checks with its callback,
 		 * but other checks with the now-opened Relation remain.
@@ -1906,6 +1974,7 @@ ExecuteTruncateGuts(List *explicit_rels,
 	foreach(cell, rels)
 	{
 		Relation	rel = (Relation) lfirst(cell);
+		LOCKMODE	lockmode;
 
 		/* Skip partitioned tables as there is nothing to do */
 		if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
@@ -1956,6 +2025,19 @@ ExecuteTruncateGuts(List *explicit_rels,
 			continue;
 		}
 
+		/*
+		 * Skip the global temporary table that is not initialized for storage
+		 * in current session.
+		 */
+		if (RELATION_IS_GLOBAL_TEMP(rel))
+		{
+			lockmode = AccessShareLock;
+			if (!gtt_storage_attached(RelationGetRelid(rel)))
+				continue;
+		}
+		else
+			lockmode = AccessExclusiveLock;
+
 		/*
 		 * Normally, we need a transaction-safe truncation here.  However, if
 		 * the table was either created in the current (sub)transaction or has
@@ -1967,7 +2049,7 @@ ExecuteTruncateGuts(List *explicit_rels,
 			rel->rd_newRelfilenodeSubid == mySubid)
 		{
 			/* Immediate, non-rollbackable truncation is OK */
-			heap_truncate_one_rel(rel);
+			heap_truncate_one_rel(rel, lockmode);
 		}
 		else
 		{
@@ -2001,7 +2083,7 @@ ExecuteTruncateGuts(List *explicit_rels,
 			if (OidIsValid(toast_relid))
 			{
 				Relation	toastrel = relation_open(toast_relid,
-													 AccessExclusiveLock);
+													 lockmode);
 
 				RelationSetNewRelfilenode(toastrel,
 										  toastrel->rd_rel->relpersistence);
@@ -2012,7 +2094,7 @@ ExecuteTruncateGuts(List *explicit_rels,
 			 * Reconstruct the indexes to match, and we're done.
 			 */
 			reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,
-							 &reindex_params);
+							 &reindex_params, lockmode);
 		}
 
 		pgstat_count_truncate(rel);
@@ -3283,6 +3365,12 @@ CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot move temporary tables of other sessions")));
 
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot move global temporary table \"%s\"",
+				 		RelationGetRelationName(rel))));
+
 	return true;
 }
 
@@ -4052,6 +4140,10 @@ AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
 	/* Caller is required to provide an adequate lock. */
 	rel = relation_open(context->relid, NoLock);
 
+	/* We allow to alter global temporary table only current session use it */
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+		CheckGlobalTempTableNotInUse(rel, "ALTER GLOBAL TEMPORARY TABLE");
+
 	CheckTableNotInUse(rel, "ALTER TABLE");
 
 	ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
@@ -5383,6 +5475,25 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
 
 			rel = table_open(tab->relid, NoLock);
 			find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
+
+			if (RELATION_IS_GLOBAL_TEMP(rel) && tab->rewrite > 0)
+			{
+				if (tab->chgPersistence)
+					ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("cannot change global temporary table persistence setting")));
+
+				if(gtt_storage_attached(tab->relid))
+					ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("cannot rewrite global temporary table \"%s\" when it has data in this session",
+								RelationGetRelationName(rel)),
+						 errhint("Please create a new connection and execute ALTER TABLE on the new connection.")));
+
+				/* global temp table has no data in this session, so only change catalog */
+				tab->rewrite = 0;
+			}
+
 			table_close(rel, NoLock);
 		}
 
@@ -5434,6 +5545,9 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("cannot rewrite temporary tables of other sessions")));
 
+			/* Not support rewrite global temp table */
+			Assert(!RELATION_IS_GLOBAL_TEMP(OldHeap));
+
 			/*
 			 * Select destination tablespace (same as original unless user
 			 * requested a change)
@@ -9081,6 +9195,12 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 						 errmsg("constraints on temporary tables must involve temporary tables of this session")));
 			break;
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_GLOBAL_TEMP)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("constraints on global temporary tables may reference only global temporary tables")));
+			break;
 	}
 
 	/*
@@ -13439,7 +13559,9 @@ TryReuseIndex(Oid oldId, IndexStmt *stmt)
 		Relation	irel = index_open(oldId, NoLock);
 
 		/* If it's a partitioned index, there is no storage to share. */
-		if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
+		/* multiple global temp table are not allow use same relfilenode */
+		if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
+			!RELATION_IS_GLOBAL_TEMP(irel))
 		{
 			stmt->oldNode = irel->rd_node.relNode;
 			stmt->oldCreateSubid = irel->rd_createSubid;
@@ -14101,6 +14223,11 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 	if (defList == NIL && operation != AT_ReplaceRelOptions)
 		return;					/* nothing to do */
 
+	/* option on_commit_delete_rows is only for global temp table and cannot be set by ALTER TABLE */
+	if (gtt_oncommit_option(defList) != ONCOMMIT_NOOP)
+		elog(ERROR, "cannot set \"on_commit_delete_rows\" for relation \"%s\"",
+					RelationGetRelationName(rel));
+
 	pgclass = table_open(RelationRelationId, RowExclusiveLock);
 
 	/* Fetch heap tuple */
@@ -14601,7 +14728,7 @@ index_copy_data(Relation rel, RelFileNode newrnode)
 	 * NOTE: any conflict in relfilenode value will be caught in
 	 * RelationCreateStorage().
 	 */
-	RelationCreateStorage(newrnode, rel->rd_rel->relpersistence);
+	RelationCreateStorage(newrnode, rel->rd_rel->relpersistence, rel);
 
 	/* copy main fork */
 	RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
@@ -16202,6 +16329,7 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
 	switch (rel->rd_rel->relpersistence)
 	{
 		case RELPERSISTENCE_TEMP:
+		case RELPERSISTENCE_GLOBAL_TEMP:
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 					 errmsg("cannot change logged status of table \"%s\" because it is temporary",
@@ -16643,7 +16771,7 @@ AlterSeqNamespaces(Relation classRel, Relation rel,
  * Register a newly-created relation's ON COMMIT action.
  */
 void
-register_on_commit_action(Oid relid, OnCommitAction action)
+register_on_commit_action(Oid relid, OnCommitAction action, bool is_gloal_temp)
 {
 	OnCommitItem *oc;
 	MemoryContext oldcxt;
@@ -16662,6 +16790,7 @@ register_on_commit_action(Oid relid, OnCommitAction action)
 	oc->oncommit = action;
 	oc->creating_subid = GetCurrentSubTransactionId();
 	oc->deleting_subid = InvalidSubTransactionId;
+	oc->is_global_temp = is_gloal_temp;
 
 	/*
 	 * We use lcons() here so that ON COMMIT actions are processed in reverse
@@ -16707,6 +16836,7 @@ PreCommit_on_commit_actions(void)
 	ListCell   *l;
 	List	   *oids_to_truncate = NIL;
 	List	   *oids_to_drop = NIL;
+	List	   *oids_to_truncate_gtt = NIL;
 
 	foreach(l, on_commits)
 	{
@@ -16730,7 +16860,12 @@ PreCommit_on_commit_actions(void)
 				 * tables, as they must still be empty.
 				 */
 				if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
-					oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
+				{
+					if (oc->is_global_temp)
+						oids_to_truncate_gtt = lappend_oid(oids_to_truncate_gtt, oc->relid);
+					else
+						oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
+				}
 				break;
 			case ONCOMMIT_DROP:
 				oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
@@ -16747,7 +16882,10 @@ PreCommit_on_commit_actions(void)
 	 * exists at truncation time.
 	 */
 	if (oids_to_truncate != NIL)
-		heap_truncate(oids_to_truncate);
+		heap_truncate(oids_to_truncate, false);
+
+	if (oids_to_truncate_gtt != NIL)
+		heap_truncate(oids_to_truncate_gtt, true);
 
 	if (oids_to_drop != NIL)
 	{
@@ -17746,6 +17884,13 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("cannot attach temporary relation of another session as partition")));
 
+	/* If the parent is permanent, so must be all of its partitions. */
+	if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot attach a global temporary relation as partition of permanent relation \"%s\"",
+						RelationGetRelationName(rel))));
+
 	/* Check if there are any columns in attachrel that aren't in the parent */
 	tupleDesc = RelationGetDescr(attachrel);
 	natts = tupleDesc->natts;
@@ -19238,3 +19383,39 @@ GetAttributeCompression(Oid atttypid, char *compression)
 
 	return cmethod;
 }
+
+/*
+ * Parse the on commit clause for the temporary table
+ */
+static OnCommitAction
+gtt_oncommit_option(List *options)
+{
+	ListCell		*listptr;
+	OnCommitAction	action = ONCOMMIT_NOOP;
+
+	foreach(listptr, options)
+	{
+		DefElem    *def = (DefElem *) lfirst(listptr);
+
+		if (strcmp(def->defname, "on_commit_delete_rows") == 0)
+		{
+			bool	res = false;
+			char	*sval = defGetString(def);
+
+			/* It has to be a Boolean value */
+			if (!parse_bool(sval, &res))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("parameter \"on_commit_delete_rows\" requires a Boolean value")));
+
+			if (res)
+				action = ONCOMMIT_DELETE_ROWS;
+			else
+				action = ONCOMMIT_PRESERVE_ROWS;
+
+			break;
+		}
+	}
+
+	return action;
+}
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 283ffaea77d..a1bee3393a0 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -39,7 +39,9 @@
 #include "catalog/pg_database.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
+#include "catalog/storage_gtt.h"
 #include "commands/cluster.h"
+#include "commands/dbcommands.h"
 #include "commands/defrem.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
@@ -1323,6 +1325,11 @@ vac_update_relstats(Relation relation,
 	Form_pg_class pgcform;
 	bool		dirty;
 
+	if (RELATION_IS_GLOBAL_TEMP(relation))
+		return vac_update_gtt_relstats(relation, num_pages, num_tuples,
+									   num_all_visible_pages, hasindex,
+									   frozenxid, minmulti, in_outer_xact);
+
 	rd = table_open(RelationRelationId, RowExclusiveLock);
 
 	/* Fetch a copy of the tuple to scribble on */
@@ -1417,7 +1424,6 @@ vac_update_relstats(Relation relation,
 	table_close(rd, RowExclusiveLock);
 }
 
-
 /*
  *	vac_update_datfrozenxid() -- update pg_database.datfrozenxid for our DB
  *
@@ -1509,6 +1515,13 @@ vac_update_datfrozenxid(void)
 			continue;
 		}
 
+		/*
+		 * The relfrozenxid for a global temporary talble is stored in localhash,
+		 * not pg_class, See list_all_session_gtt_frozenxids()
+		 */
+		if (classForm->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+			continue;
+
 		/*
 		 * Some table AMs might not need per-relation xid / multixid horizons.
 		 * It therefore seems reasonable to allow relfrozenxid and relminmxid
@@ -1566,6 +1579,42 @@ vac_update_datfrozenxid(void)
 	Assert(TransactionIdIsNormal(newFrozenXid));
 	Assert(MultiXactIdIsValid(newMinMulti));
 
+	/* If enable global temporary table */
+	if (max_active_gtt > 0)
+	{
+		TransactionId	safe_age;
+		TransactionId	oldest_gtt_frozenxid =
+			gtt_get_oldest_frozenxids_in_current_database(NULL, NULL);
+
+		if (TransactionIdIsNormal(oldest_gtt_frozenxid))
+		{
+			safe_age = oldest_gtt_frozenxid + vacuum_gtt_defer_check_age;
+			if (safe_age < FirstNormalTransactionId)
+				safe_age += FirstNormalTransactionId;
+
+			/*
+			 * We tolerate that the minimum age of gtt is less than
+			 * the minimum age of conventional tables, otherwise it will
+			 * throw warning message.
+			 */
+			if (TransactionIdIsNormal(safe_age) &&
+				TransactionIdPrecedes(safe_age, newFrozenXid))
+			{
+				ereport(WARNING,
+						(errmsg("global temporary table oldest relfrozenxid %u is far in the past",
+								oldest_gtt_frozenxid),
+						 errdetail("The oldest relfrozenxid %u in database \"%s\"", newFrozenXid, get_database_name(MyDatabaseId)),
+						 errhint("please consider cleaning up the data in global temporary table to avoid wraparound problems.")));
+			}
+
+			/*
+			 * We need to ensure that the clog required by gtt is not cleand.
+			 */
+			if (TransactionIdPrecedes(oldest_gtt_frozenxid, newFrozenXid))
+				newFrozenXid = oldest_gtt_frozenxid;
+		}
+	}
+
 	/* Now fetch the pg_database tuple we need to update. */
 	relation = table_open(DatabaseRelationId, RowExclusiveLock);
 
@@ -1917,6 +1966,19 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
 		return false;
 	}
 
+	/*
+	 * Skip those global temporary table that are not initialized in
+	 * this session.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(rel) &&
+		!gtt_storage_attached(RelationGetRelid(rel)))
+	{
+		relation_close(rel, lmode);
+		PopActiveSnapshot();
+		CommitTransactionCommand();
+		return false;
+	}
+
 	/*
 	 * Silently ignore tables that are temp tables of other backends ---
 	 * trying to vacuum these will lead to great unhappiness, since their
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e183ab097c4..04ae8ff7073 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -527,6 +527,12 @@ DefineView(ViewStmt *stmt, const char *queryString,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("views cannot be unlogged because they do not have storage")));
 
+	/* Global temporary table are not sensible. */
+	if (stmt->view->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("views cannot be global temporary because they do not have storage")));
+
 	/*
 	 * If the user didn't explicitly ask for a temporary view, check whether
 	 * we need one implicitly.  We allow TEMP to be inserted automatically as
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 549d9eb6963..370e4d09d1d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -784,6 +784,10 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 		if (isTempNamespace(get_rel_namespace(rte->relid)))
 			continue;
 
+		/* Global temp table is one kind of temp table */
+		if (get_rel_persistence(rte->relid) == RELPERSISTENCE_GLOBAL_TEMP)
+			continue;
+
 		PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt));
 	}
 
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 9df1f81ea89..2207dccf274 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -50,6 +50,7 @@
 #include "access/table.h"
 #include "access/tableam.h"
 #include "access/transam.h"
+#include "catalog/storage_gtt.h"
 #include "executor/executor.h"
 #include "executor/execPartition.h"
 #include "jit/jit.h"
@@ -832,7 +833,7 @@ ExecGetRangeTableRelation(EState *estate, Index rti)
  */
 void
 ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo,
-					   Index rti)
+					   Index rti, CmdType operation)
 {
 	Relation	resultRelationDesc;
 
@@ -843,6 +844,9 @@ ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo,
 					  NULL,
 					  estate->es_instrument);
 
+	/* Check and init global temporary table storage in this session */
+	gtt_init_storage(operation, resultRelationDesc);
+
 	if (estate->es_result_relations == NULL)
 		estate->es_result_relations = (ResultRelInfo **)
 			palloc0(estate->es_range_table_size * sizeof(ResultRelInfo *));
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 5ec699a9bd1..1401d999b40 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,6 +38,7 @@
 #include "access/tableam.h"
 #include "access/xact.h"
 #include "catalog/catalog.h"
+#include "catalog/storage_gtt.h"
 #include "commands/trigger.h"
 #include "executor/execPartition.h"
 #include "executor/executor.h"
@@ -2754,13 +2755,13 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	{
 		mtstate->rootResultRelInfo = makeNode(ResultRelInfo);
 		ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
-							   node->rootRelation);
+							   node->rootRelation, operation);
 	}
 	else
 	{
 		mtstate->rootResultRelInfo = mtstate->resultRelInfo;
 		ExecInitResultRelation(estate, mtstate->resultRelInfo,
-							   linitial_int(node->resultRelations));
+							   linitial_int(node->resultRelations), operation);
 	}
 
 	/* set up epqstate with dummy subplan data for the moment */
@@ -2788,7 +2789,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 
 		if (resultRelInfo != mtstate->rootResultRelInfo)
 		{
-			ExecInitResultRelation(estate, resultRelInfo, resultRelation);
+			ExecInitResultRelation(estate, resultRelInfo, resultRelation, operation);
 
 			/*
 			 * For child result relations, store the root result relation
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 169b1d53fc8..07a616d626b 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -48,7 +48,7 @@
 #include "partitioning/partprune.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
-
+#include "utils/rel.h"
 
 /* results of subquery_is_pushdown_safe */
 typedef struct pushdown_safety_info
@@ -619,7 +619,7 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
 			 * the rest of the necessary infrastructure right now anyway.  So
 			 * for now, bail out if we see a temporary table.
 			 */
-			if (get_rel_persistence(rte->relid) == RELPERSISTENCE_TEMP)
+			if (RelpersistenceTsTemp(get_rel_persistence(rte->relid)))
 				return;
 
 			/*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index bd09f85aea1..a56f0b8ceee 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6071,7 +6071,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid)
 	 * Furthermore, any index predicate or index expressions must be parallel
 	 * safe.
 	 */
-	if (heap->rd_rel->relpersistence == RELPERSISTENCE_TEMP ||
+	if (RELATION_IS_TEMP(heap) ||
 		!is_parallel_safe(root, (Node *) RelationGetIndexExpressions(index)) ||
 		!is_parallel_safe(root, (Node *) RelationGetIndexPredicate(index)))
 	{
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index a5002ad8955..22f64506caa 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -31,6 +31,7 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_statistic_ext.h"
 #include "catalog/pg_statistic_ext_data.h"
+#include "catalog/storage_gtt.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -222,6 +223,14 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 				continue;
 			}
 
+			/* Ignore empty index for global temporary table in this session */
+			if (RELATION_IS_GLOBAL_TEMP(indexRelation) &&
+				!gtt_storage_attached(RelationGetRelid(indexRelation)))
+			{
+				index_close(indexRelation, NoLock);
+				continue;
+			}
+
 			/*
 			 * If the index is valid, but cannot yet be used, ignore it; but
 			 * mark the plan we are generating as transient. See
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6ac2e9ce237..2d62d0a4036 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -2902,7 +2902,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
 		 * creation query. It would be hard to refresh data or incrementally
 		 * maintain it if a source disappeared.
 		 */
-		if (isQueryUsingTempRelation(query))
+		if (isQueryUsingTempRelation(query) || isQueryUsingGlobalTempRelation(query))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("materialized views must not use temporary tables or views")));
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b5966712ce1..388303a2d85 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3434,17 +3434,11 @@ OptTemp:	TEMPORARY					{ $$ = RELPERSISTENCE_TEMP; }
 			| LOCAL TEMP				{ $$ = RELPERSISTENCE_TEMP; }
 			| GLOBAL TEMPORARY
 				{
-					ereport(WARNING,
-							(errmsg("GLOBAL is deprecated in temporary table creation"),
-							 parser_errposition(@1)));
-					$$ = RELPERSISTENCE_TEMP;
+					$$ = RELPERSISTENCE_GLOBAL_TEMP;
 				}
 			| GLOBAL TEMP
 				{
-					ereport(WARNING,
-							(errmsg("GLOBAL is deprecated in temporary table creation"),
-							 parser_errposition(@1)));
-					$$ = RELPERSISTENCE_TEMP;
+					$$ = RELPERSISTENCE_GLOBAL_TEMP;
 				}
 			| UNLOGGED					{ $$ = RELPERSISTENCE_UNLOGGED; }
 			| /*EMPTY*/					{ $$ = RELPERSISTENCE_PERMANENT; }
@@ -11843,19 +11837,13 @@ OptTempTableName:
 				}
 			| GLOBAL TEMPORARY opt_table qualified_name
 				{
-					ereport(WARNING,
-							(errmsg("GLOBAL is deprecated in temporary table creation"),
-							 parser_errposition(@1)));
 					$$ = $4;
-					$$->relpersistence = RELPERSISTENCE_TEMP;
+					$$->relpersistence = RELPERSISTENCE_GLOBAL_TEMP;
 				}
 			| GLOBAL TEMP opt_table qualified_name
 				{
-					ereport(WARNING,
-							(errmsg("GLOBAL is deprecated in temporary table creation"),
-							 parser_errposition(@1)));
 					$$ = $4;
-					$$->relpersistence = RELPERSISTENCE_TEMP;
+					$$->relpersistence = RELPERSISTENCE_GLOBAL_TEMP;
 				}
 			| UNLOGGED opt_table qualified_name
 				{
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index cb9e177b5e5..e333f427740 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -82,6 +82,7 @@ static void expandTupleDesc(TupleDesc tupdesc, Alias *eref,
 							List **colnames, List **colvars);
 static int	specialAttNum(const char *attname);
 static bool isQueryUsingTempRelation_walker(Node *node, void *context);
+static bool isQueryUsingGlobalTempRelation_walker(Node *node, void *context);
 
 
 /*
@@ -3665,3 +3666,52 @@ isQueryUsingTempRelation_walker(Node *node, void *context)
 								  isQueryUsingTempRelation_walker,
 								  context);
 }
+
+/*
+ * Like function isQueryUsingTempRelation_walker
+ * return true if any relation underlying
+ * the query is a global temporary table.
+ */
+static bool
+isQueryUsingGlobalTempRelation_walker(Node *node, void *context)
+{
+	if (node == NULL)
+		return false;
+
+	if (IsA(node, Query))
+	{
+		Query	   *query = (Query *) node;
+		ListCell   *rtable;
+
+		foreach(rtable, query->rtable)
+		{
+			RangeTblEntry *rte = lfirst(rtable);
+
+			if (rte->rtekind == RTE_RELATION)
+			{
+				Relation	rel = table_open(rte->relid, AccessShareLock);
+				char		relpersistence = rel->rd_rel->relpersistence;
+
+				table_close(rel, AccessShareLock);
+				if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+					return true;
+			}
+		}
+
+		return query_tree_walker(query,
+								 isQueryUsingGlobalTempRelation_walker,
+								 context,
+								 QTW_IGNORE_JOINALIASES);
+	}
+
+	return expression_tree_walker(node,
+								  isQueryUsingGlobalTempRelation_walker,
+								  context);
+}
+
+/* Check if the query uses global temporary table */
+bool
+isQueryUsingGlobalTempRelation(Query *query)
+{
+	return isQueryUsingGlobalTempRelation_walker((Node *) query, NULL);
+}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0eea214dd89..00233b13658 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -447,6 +447,13 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
 	seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 	seqstmt->options = seqoptions;
 
+	/*
+	 * If a sequence is bound to a global temporary table, then the sequence
+	 * must been "global temporary"
+	 */
+	if (cxt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		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
@@ -3326,6 +3333,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 		cxt.isforeign = false;
 	}
 	cxt.relation = stmt->relation;
+	/* Set the relpersistence to the context */
+	cxt.relation->relpersistence = RelationGetRelPersistence(rel);
 	cxt.rel = rel;
 	cxt.inhRelations = NIL;
 	cxt.isalter = true;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 681ef91b81e..4ea4c5bfcc7 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2116,6 +2116,14 @@ do_autovacuum(void)
 			}
 			continue;
 		}
+		else if (classForm->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		{
+			/*
+			 * Aotuvacuum cannot vacuum the private data stored in each session
+			 * that belongs to global temporary table, so skip them.
+			 */
+			continue;
+		}
 
 		/* Fetch reloptions and the pgstat entry for this table */
 		relopts = extract_autovac_opts(tuple, pg_class_desc);
@@ -2182,7 +2190,7 @@ do_autovacuum(void)
 		/*
 		 * We cannot safely process other backends' temp tables, so skip 'em.
 		 */
-		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+		if (RelpersistenceTsTemp(classForm->relpersistence))
 			continue;
 
 		relid = classForm->oid;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index a2512e750c2..9aefdf48cb9 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -37,6 +37,7 @@
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
 #include "catalog/storage.h"
+#include "catalog/storage_gtt.h"
 #include "executor/instrument.h"
 #include "lib/binaryheap.h"
 #include "miscadmin.h"
@@ -2934,7 +2935,15 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
 BlockNumber
 RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 {
-	if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
+	/*
+	 * Returns 0 if this global temporary table is not initialized in this session.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(relation) &&
+		!gtt_storage_attached(RelationGetRelid(relation)))
+	{
+		return 0;
+	}
+	else if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
 	{
 		/*
 		 * Not every table AM uses BLCKSZ wide fixed size blocks.
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 9f26e41c464..0a25b151e82 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -22,6 +22,7 @@
 #include "access/subtrans.h"
 #include "access/syncscan.h"
 #include "access/twophase.h"
+#include "catalog/storage_gtt.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -143,6 +144,7 @@ CalculateShmemSize(int *num_semaphores)
 	size = add_size(size, BTreeShmemSize());
 	size = add_size(size, SyncScanShmemSize());
 	size = add_size(size, AsyncShmemSize());
+	size = add_size(size, active_gtt_shared_hash_size());
 #ifdef EXEC_BACKEND
 	size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -246,6 +248,8 @@ CreateSharedMemoryAndSemaphores(void)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	/* For global temporary table shared hashtable */
+	active_gtt_shared_hash_init();
 
 	/*
 	 * Set up lock manager
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 3be60402890..dfb78547c0a 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -65,6 +65,7 @@
 #include "utils/builtins.h"
 #include "utils/rel.h"
 #include "utils/snapmgr.h"
+#include "utils/guc.h"
 
 #define UINT32_ACCESS_ONCE(var)		 ((uint32)(*((volatile uint32 *)&(var))))
 
@@ -5161,3 +5162,66 @@ KnownAssignedXidsReset(void)
 
 	LWLockRelease(ProcArrayLock);
 }
+
+/*
+ * Search all active session to get db level oldest frozenxid
+ * for global temporary table.
+ *
+ * Pids and Xids are used to store the session level oldest frozenxid if specified
+ */
+TransactionId
+gtt_get_oldest_frozenxids_in_current_database(List **pids, List **xids)
+{
+	ProcArrayStruct		*arrayP = NULL;
+	TransactionId		result = InvalidTransactionId;
+	int			index = 0;
+	int			i = 0;
+	uint8		flags = 0;
+
+	/* return 0 if feature is disabled */
+	if (max_active_gtt <= 0)
+		return InvalidTransactionId;
+
+	/* Disable in standby node */
+	if (RecoveryInProgress())
+		return InvalidTransactionId;
+
+	flags |= PROC_IS_AUTOVACUUM;
+	flags |= PROC_IN_LOGICAL_DECODING;
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+	arrayP = procArray;
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		int			pgprocno = arrayP->pgprocnos[index];
+		PGPROC	   *proc = &allProcs[pgprocno];
+		uint8		statusFlags = ProcGlobal->statusFlags[index];
+		TransactionId	gtt_frozenxid = InvalidTransactionId;
+
+		if (statusFlags & flags)
+			continue;
+
+		/* Fetch all session level frozenxid that is belonging to current database */
+		gtt_frozenxid = proc->gtt_frozenxid;
+		if (proc->databaseId == MyDatabaseId &&
+			TransactionIdIsNormal(gtt_frozenxid))
+		{
+			if (result == InvalidTransactionId)
+				result = gtt_frozenxid;
+			else if (TransactionIdPrecedes(gtt_frozenxid, result))
+				result = gtt_frozenxid;
+
+			/* save backend pid and session level oldest relfrozenxid */
+			if (pids)
+				*pids = lappend_int(*pids, proc->pid);
+
+			if (xids)
+				*xids = lappend_oid(*xids, gtt_frozenxid);
+
+			i++;
+		}
+	}
+	LWLockRelease(ProcArrayLock);
+
+	return result;
+}
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 7b0dea4abec..ab0d2031e86 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -176,7 +176,9 @@ static const char *const BuiltinTrancheNames[] = {
 	/* LWTRANCHE_PARALLEL_APPEND: */
 	"ParallelAppend",
 	/* LWTRANCHE_PER_XACT_PREDICATE_LIST: */
-	"PerXactPredicateList"
+	"PerXactPredicateList",
+	/* LWTRANCHE_GLOBAL_TEMP_TABLE_CTL */
+	"GlobalTempTableControl"
 };
 
 StaticAssertDecl(lengthof(BuiltinTrancheNames) ==
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index e306b04738d..c9e90714c3b 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -391,6 +391,7 @@ InitProcess(void)
 	MyProc->databaseId = InvalidOid;
 	MyProc->roleId = InvalidOid;
 	MyProc->tempNamespaceId = InvalidOid;
+	MyProc->gtt_frozenxid = InvalidTransactionId;	/* init session level gtt frozenxid */
 	MyProc->isBackgroundWorker = IsBackgroundWorker;
 	MyProc->delayChkpt = false;
 	MyProc->statusFlags = 0;
@@ -576,6 +577,7 @@ InitAuxiliaryProcess(void)
 	MyProc->databaseId = InvalidOid;
 	MyProc->roleId = InvalidOid;
 	MyProc->tempNamespaceId = InvalidOid;
+	MyProc->gtt_frozenxid = InvalidTransactionId;	/* init session level gtt frozenxid */
 	MyProc->isBackgroundWorker = IsBackgroundWorker;
 	MyProc->delayChkpt = false;
 	MyProc->statusFlags = 0;
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 3a2f2e1f99d..9a6e7cee244 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -982,6 +982,13 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 				Assert(backend != InvalidBackendId);
 			}
 			break;
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			/*
+			 * For global temporary table ,each backend has its own storage,
+			 * also only sees its own storage. Use Backendid to identify them.
+			 */
+			backend = BackendIdForTempRelations();
+			break;
 		default:
 			elog(ERROR, "invalid relpersistence: %c", relform->relpersistence);
 			backend = InvalidBackendId; /* placate compiler */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 1fbb0b28c3b..637ff7f7e43 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -108,6 +108,7 @@
 #include "catalog/pg_operator.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_statistic_ext.h"
+#include "catalog/storage_gtt.h"
 #include "executor/nodeAgg.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -5117,12 +5118,26 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
 						}
 						else if (index->indpred == NIL)
 						{
-							vardata->statsTuple =
-								SearchSysCache3(STATRELATTINH,
-												ObjectIdGetDatum(index->indexoid),
-												Int16GetDatum(pos + 1),
-												BoolGetDatum(false));
-							vardata->freefunc = ReleaseSysCache;
+							char rel_persistence = get_rel_persistence(index->indexoid);
+
+							if (rel_persistence == RELPERSISTENCE_GLOBAL_TEMP)
+							{
+								/* For global temporary table, get statistic data from localhash */
+								vardata->statsTuple =
+									get_gtt_att_statistic(index->indexoid,
+														Int16GetDatum(pos + 1),
+														false);
+								vardata->freefunc = release_gtt_statistic_cache;
+							}
+							else
+							{
+								vardata->statsTuple =
+									SearchSysCache3(STATRELATTINH,
+													ObjectIdGetDatum(index->indexoid),
+													Int16GetDatum(pos + 1),
+													BoolGetDatum(false));
+								vardata->freefunc = ReleaseSysCache;
+							}
 
 							if (HeapTupleIsValid(vardata->statsTuple))
 							{
@@ -5365,15 +5380,28 @@ examine_simple_variable(PlannerInfo *root, Var *var,
 	}
 	else if (rte->rtekind == RTE_RELATION)
 	{
-		/*
-		 * Plain table or parent of an inheritance appendrel, so look up the
-		 * column in pg_statistic
-		 */
-		vardata->statsTuple = SearchSysCache3(STATRELATTINH,
-											  ObjectIdGetDatum(rte->relid),
-											  Int16GetDatum(var->varattno),
-											  BoolGetDatum(rte->inh));
-		vardata->freefunc = ReleaseSysCache;
+		char rel_persistence = get_rel_persistence(rte->relid);
+
+		if (rel_persistence == RELPERSISTENCE_GLOBAL_TEMP)
+		{
+			/* For global temporary table, get statistic data from localhash */
+			vardata->statsTuple = get_gtt_att_statistic(rte->relid,
+														var->varattno,
+														rte->inh);
+			vardata->freefunc = release_gtt_statistic_cache;
+		}
+		else
+		{
+			/*
+			 * Plain table or parent of an inheritance appendrel, so look up the
+			 * column in pg_statistic
+			 */
+			vardata->statsTuple = SearchSysCache3(STATRELATTINH,
+												  ObjectIdGetDatum(rte->relid),
+												  Int16GetDatum(var->varattno),
+												  BoolGetDatum(rte->inh));
+			vardata->freefunc = ReleaseSysCache;
+		}
 
 		if (HeapTupleIsValid(vardata->statsTuple))
 		{
@@ -6817,6 +6845,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	{
 		/* Simple variable --- look to stats for the underlying table */
 		RangeTblEntry *rte = planner_rt_fetch(index->rel->relid, root);
+		char	rel_persistence = get_rel_persistence(rte->relid);
 
 		Assert(rte->rtekind == RTE_RELATION);
 		relid = rte->relid;
@@ -6834,6 +6863,14 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 				!vardata.freefunc)
 				elog(ERROR, "no function provided to release variable stats with");
 		}
+		else if (rel_persistence == RELPERSISTENCE_GLOBAL_TEMP)
+		{
+			/* For global temporary table, get statistic data from localhash */
+			vardata.statsTuple = get_gtt_att_statistic(relid,
+													colnum,
+													rte->inh);
+			vardata.freefunc = release_gtt_statistic_cache;
+		}
 		else
 		{
 			vardata.statsTuple = SearchSysCache3(STATRELATTINH,
@@ -6845,6 +6882,8 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	}
 	else
 	{
+		char	rel_persistence = get_rel_persistence(index->indexoid);
+
 		/* Expression --- maybe there are stats for the index itself */
 		relid = index->indexoid;
 		colnum = 1;
@@ -6860,6 +6899,14 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 				!vardata.freefunc)
 				elog(ERROR, "no function provided to release variable stats with");
 		}
+		else if (rel_persistence == RELPERSISTENCE_GLOBAL_TEMP)
+		{
+			/* For global temporary table, get statistic data from localhash */
+			vardata.statsTuple = get_gtt_att_statistic(relid,
+													colnum,
+													false);
+			vardata.freefunc = release_gtt_statistic_cache;
+		}
 		else
 		{
 			vardata.statsTuple = SearchSysCache3(STATRELATTINH,
@@ -7778,6 +7825,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 		/* attempt to lookup stats in relation for this index column */
 		if (attnum != 0)
 		{
+			char	rel_persistence = get_rel_persistence(rte->relid);
+
 			/* Simple variable -- look to stats for the underlying table */
 			if (get_relation_stats_hook &&
 				(*get_relation_stats_hook) (root, rte, attnum, &vardata))
@@ -7790,6 +7839,15 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 					elog(ERROR,
 						 "no function provided to release variable stats with");
 			}
+			else if (rel_persistence == RELPERSISTENCE_GLOBAL_TEMP)
+			{
+				/* For global temporary table, get statistic data from localhash */
+				vardata.statsTuple =
+					get_gtt_att_statistic(rte->relid,
+										attnum,
+										false);
+				vardata.freefunc = release_gtt_statistic_cache;
+			}
 			else
 			{
 				vardata.statsTuple =
@@ -7802,6 +7860,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 		}
 		else
 		{
+			char	rel_persistence = get_rel_persistence(index->indexoid);
+
 			/*
 			 * Looks like we've found an expression column in the index. Let's
 			 * see if there's any stats for it.
@@ -7821,6 +7881,15 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 					!vardata.freefunc)
 					elog(ERROR, "no function provided to release variable stats with");
 			}
+			else if (rel_persistence == RELPERSISTENCE_GLOBAL_TEMP)
+			{
+				/* For global temporary table, get statistic data from localhash */
+				vardata.statsTuple =
+					get_gtt_att_statistic(index->indexoid,
+										attnum,
+										false);
+				vardata.freefunc = release_gtt_statistic_cache;
+			}
 			else
 			{
 				vardata.statsTuple = SearchSysCache3(STATRELATTINH,
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index feef9998634..ebaad33a698 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -35,6 +35,7 @@
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_transform.h"
 #include "catalog/pg_type.h"
+#include "catalog/storage_gtt.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "utils/array.h"
@@ -3113,6 +3114,19 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
 		if (stawidth > 0)
 			return stawidth;
 	}
+	if (get_rel_persistence(relid) == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		/* For global temporary table, get statistic data from localhash */
+		tp = get_gtt_att_statistic(relid, attnum, false);
+		if (!HeapTupleIsValid(tp))
+			return 0;
+
+		stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
+		if (stawidth > 0)
+			return stawidth;
+		else
+			return 0;
+	}
 	tp = SearchSysCache3(STATRELATTINH,
 						 ObjectIdGetDatum(relid),
 						 Int16GetDatum(attnum),
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 2e760e8a3bd..758a635b75e 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -65,6 +65,7 @@
 #include "catalog/pg_type.h"
 #include "catalog/schemapg.h"
 #include "catalog/storage.h"
+#include "catalog/storage_gtt.h"
 #include "commands/policy.h"
 #include "commands/trigger.h"
 #include "miscadmin.h"
@@ -1152,6 +1153,36 @@ retry:
 				relation->rd_islocaltemp = false;
 			}
 			break;
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			{
+				BlockNumber     relpages = 0;
+				double          reltuples = 0;
+				BlockNumber     relallvisible = 0;
+				TransactionId	relfrozenxid = InvalidTransactionId;
+				MultiXactId 	relminmxid = InvalidMultiXactId;
+
+				relation->rd_backend = BackendIdForTempRelations();
+				relation->rd_islocaltemp = false;
+
+				/*
+				 * For global temporary table
+				 * get session level relstats from localhash
+				 * and set it to local relcache
+				 */
+				get_gtt_relstats(RelationGetRelid(relation),
+								 &relpages, &reltuples, &relallvisible,
+								 &relfrozenxid, &relminmxid);
+
+				relation->rd_rel->relpages = (int32)relpages;
+				relation->rd_rel->reltuples = (float4)reltuples;
+				relation->rd_rel->relallvisible = (int32)relallvisible;
+				if (TransactionIdIsNormal(relfrozenxid))
+					relation->rd_rel->relfrozenxid = relfrozenxid;
+
+				if (MultiXactIdIsValid(relminmxid))
+					relation->rd_rel->relminmxid = relminmxid;
+			}
+			break;
 		default:
 			elog(ERROR, "invalid relpersistence: %c",
 				 relation->rd_rel->relpersistence);
@@ -1212,6 +1243,15 @@ retry:
 	else
 		Assert(relation->rd_rel->relam == InvalidOid);
 
+	/*
+	 * For one global temporary table,
+	 * other session may created one index, that triggers relcache reload for this session.
+	 * If table already has data at this session, to avoid rebuilding index,
+	 * accept the structure of the index but set the local state to indisvalid.
+	 */
+	if (relation->rd_rel->relkind == RELKIND_INDEX)
+		gtt_correct_index_session_state(relation);
+
 	/* extract reloptions if any */
 	RelationParseRelOptions(relation, pg_class_tuple);
 
@@ -1335,7 +1375,23 @@ RelationInitPhysicalAddr(Relation relation)
 			heap_freetuple(phys_tuple);
 		}
 
-		relation->rd_node.relNode = relation->rd_rel->relfilenode;
+		/*
+		 * For global temporary table,
+		 * the latest relfilenode is saved in localHash(see RelationSetNewRelfilenode()),
+		 * get it and put it to local relcache.
+		 */
+		if (RELATION_IS_GLOBAL_TEMP(relation))
+		{
+			Oid newrelnode = gtt_fetch_current_relfilenode(RelationGetRelid(relation));
+
+			if (OidIsValid(newrelnode) &&
+				newrelnode != relation->rd_rel->relfilenode)
+				relation->rd_node.relNode = newrelnode;
+			else
+				relation->rd_node.relNode = relation->rd_rel->relfilenode;
+		}
+		else
+			relation->rd_node.relNode = relation->rd_rel->relfilenode;
 	}
 	else
 	{
@@ -2288,6 +2344,14 @@ RelationReloadIndexInfo(Relation relation)
 							   HeapTupleHeaderGetXmin(tuple->t_data));
 
 		ReleaseSysCache(tuple);
+
+		/*
+		 * For one global temporary table,
+		 * other session may created one index, that triggers relcache reload for this session.
+		 * If table already has data at this session, to avoid rebuilding index,
+		 * accept the structure of the index but set the local state to indisvalid.
+		 */
+		gtt_correct_index_session_state(relation);
 	}
 
 	/* Okay, now it's valid again */
@@ -3568,6 +3632,10 @@ RelationBuildLocalRelation(const char *relname,
 			rel->rd_backend = BackendIdForTempRelations();
 			rel->rd_islocaltemp = true;
 			break;
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			rel->rd_backend = BackendIdForTempRelations();
+			rel->rd_islocaltemp = false;
+			break;
 		default:
 			elog(ERROR, "invalid relpersistence: %c", relpersistence);
 			break;
@@ -3681,6 +3749,13 @@ RelationSetNewRelfilenode(Relation relation, char persistence)
 	TransactionId freezeXid = InvalidTransactionId;
 	RelFileNode newrnode;
 
+	/*
+	 * For global temporary table, storage information for each session is
+	 * maintained locally, not in pg_class.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(relation))
+		return GlobalTempRelationSetNewRelfilenode(relation);
+
 	/* Allocate a new relfilenode */
 	newrelfilenode = GetNewRelFileNode(relation->rd_rel->reltablespace, NULL,
 									   persistence);
@@ -3724,7 +3799,7 @@ RelationSetNewRelfilenode(Relation relation, char persistence)
 		/* handle these directly, at least for now */
 		SMgrRelation srel;
 
-		srel = RelationCreateStorage(newrnode, persistence);
+		srel = RelationCreateStorage(newrnode, persistence, relation);
 		smgrclose(srel);
 	}
 	else
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 4c94f09c645..b4017794a35 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -44,6 +44,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "catalog/storage.h"
+#include "catalog/storage_gtt.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
 #include "commands/tablespace.h"
@@ -154,6 +155,18 @@ char	   *GUC_check_errmsg_string;
 char	   *GUC_check_errdetail_string;
 char	   *GUC_check_errhint_string;
 
+/*
+ * num = 0 means disable global temporary table feature.
+ * table schema are still saved in catalog.
+ *
+ * num > 0 means allows the database to manage multiple active tables at the same time.
+ */
+#define	MIN_NUM_ACTIVE_GTT			0
+#define	DEFAULT_NUM_ACTIVE_GTT			1000
+#define	MAX_NUM_ACTIVE_GTT			1000000
+
+int		max_active_gtt = MIN_NUM_ACTIVE_GTT;
+
 static void do_serialize(char **destptr, Size *maxbytes, const char *fmt,...) pg_attribute_printf(3, 4);
 
 static void set_config_sourcefile(const char *name, char *sourcefile,
@@ -2141,6 +2154,15 @@ static struct config_bool ConfigureNamesBool[] =
 
 static struct config_int ConfigureNamesInt[] =
 {
+	{
+		{"max_active_global_temporary_table", PGC_POSTMASTER, UNGROUPED,
+			gettext_noop("max active global temporary table."),
+			NULL
+		},
+		&max_active_gtt,
+		DEFAULT_NUM_ACTIVE_GTT, MIN_NUM_ACTIVE_GTT, MAX_NUM_ACTIVE_GTT,
+		NULL, NULL, NULL
+	},
 	{
 		{"archive_timeout", PGC_SIGHUP, WAL_ARCHIVING,
 			gettext_noop("Sets the amount of time to wait before forcing a "
@@ -2712,6 +2734,16 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"vacuum_gtt_defer_check_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("The defer check age of GTT, used to check expired data after vacuum."),
+			NULL
+		},
+		&vacuum_gtt_defer_check_age,
+		10000, 0, 1000000,
+		NULL, NULL, NULL
+	},
+
 	/*
 	 * See also CheckRequiredParameterValues() if this parameter changes
 	 */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7c2f1d30447..cb18546ad74 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -2538,6 +2538,10 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
 		dopt->no_unlogged_table_data)
 		return;
 
+	/* Don't dump data in global temporary table/sequence */
+	if (tbinfo->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		return;
+
 	/* Check that the data is not explicitly excluded */
 	if (simple_oid_list_member(&tabledata_exclude_oids,
 							   tbinfo->dobj.catId.oid))
@@ -14913,6 +14917,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
 		char	   *ftoptions = NULL;
 		char	   *srvname = NULL;
 		char	   *foreign = "";
+		char	   *table_type = NULL;
 
 		/*
 		 * Set reltypename, and collect any relkind-specific data that we
@@ -14988,9 +14993,15 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
 			binary_upgrade_set_pg_class_oids(fout, q,
 											 tbinfo->dobj.catId.oid, false);
 
+		if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED)
+			table_type = "UNLOGGED ";
+		else if (tbinfo->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+			table_type = "GLOBAL TEMPORARY ";
+		else
+			table_type = "";
+
 		appendPQExpBuffer(q, "CREATE %s%s %s",
-						  tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
-						  "UNLOGGED " : "",
+						  table_type,
 						  reltypename,
 						  qualrelname);
 
@@ -15358,6 +15369,15 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
 			}
 		}
 
+		/*
+		 * Transaction information for the global temporary table is not stored
+		 * in the pg_class.
+		 */
+		if (tbinfo->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		{
+			Assert(tbinfo->frozenxid == 0);
+			Assert(tbinfo->minmxid == 0);
+		}
 		/*
 		 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
 		 * relminmxid of all vacuumable relations.  (While vacuum.c processes
@@ -15365,7 +15385,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
 		 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
 		 * child toast table is handled below.)
 		 */
-		if (dopt->binary_upgrade &&
+		else if (dopt->binary_upgrade &&
 			(tbinfo->relkind == RELKIND_RELATION ||
 			 tbinfo->relkind == RELKIND_MATVIEW))
 		{
@@ -16385,6 +16405,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	PQExpBuffer query = createPQExpBuffer();
 	PQExpBuffer delqry = createPQExpBuffer();
 	char	   *qseqname;
+	bool	   global_temp_seq = false;
 
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
@@ -16394,9 +16415,12 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 						  "SELECT format_type(seqtypid, NULL), "
 						  "seqstart, seqincrement, "
 						  "seqmax, seqmin, "
-						  "seqcache, seqcycle "
-						  "FROM pg_catalog.pg_sequence "
-						  "WHERE seqrelid = '%u'::oid",
+						  "seqcache, seqcycle, "
+						  "c.relpersistence "
+						  "FROM pg_catalog.pg_sequence s, "
+						  "pg_catalog.pg_class c "
+						  "WHERE seqrelid = '%u'::oid "
+						  "and s.seqrelid = c.oid",
 						  tbinfo->dobj.catId.oid);
 	}
 	else
@@ -16433,6 +16457,9 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	cache = PQgetvalue(res, 0, 5);
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
+	if (fout->remoteVersion >= 150000)
+		global_temp_seq = (strcmp(PQgetvalue(res, 0, 7), "g") == 0);
+
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (incby[0] != '-');
 	if (strcmp(seqtype, "smallint") == 0)
@@ -16510,9 +16537,13 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	}
 	else
 	{
-		appendPQExpBuffer(query,
-						  "CREATE SEQUENCE %s\n",
-						  fmtQualifiedDumpable(tbinfo));
+		appendPQExpBuffer(query, "CREATE ");
+
+		if (global_temp_seq)
+			appendPQExpBuffer(query, "GLOBAL TEMP ");
+
+		appendPQExpBuffer(query, "SEQUENCE %s\n",
+								  fmtQualifiedDumpable(tbinfo));
 
 		if (strcmp(seqtype, "bigint") != 0)
 			appendPQExpBuffer(query, "    AS %s\n", seqtype);
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 3d218c2ad24..b120ff8e71d 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -88,7 +88,7 @@ check_and_dump_old_cluster(bool live_check)
 		start_postmaster(&old_cluster, true);
 
 	/* Extract a list of databases and tables from the old cluster */
-	get_db_and_rel_infos(&old_cluster);
+	get_db_and_rel_infos(&old_cluster, true);
 
 	init_tablespaces();
 
@@ -174,7 +174,7 @@ check_and_dump_old_cluster(bool live_check)
 void
 check_new_cluster(void)
 {
-	get_db_and_rel_infos(&new_cluster);
+	get_db_and_rel_infos(&new_cluster, false);
 
 	check_new_cluster_is_empty();
 	check_databases_are_compatible();
diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c
index f7fa0820d69..028e0700e37 100644
--- a/src/bin/pg_upgrade/info.c
+++ b/src/bin/pg_upgrade/info.c
@@ -21,7 +21,7 @@ static void report_unmatched_relation(const RelInfo *rel, const DbInfo *db,
 									  bool is_new_db);
 static void free_db_and_rel_infos(DbInfoArr *db_arr);
 static void get_db_infos(ClusterInfo *cluster);
-static void get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo);
+static void get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo, bool skip_global_temp);
 static void free_rel_infos(RelInfoArr *rel_arr);
 static void print_db_infos(DbInfoArr *dbinfo);
 static void print_rel_infos(RelInfoArr *rel_arr);
@@ -271,9 +271,11 @@ report_unmatched_relation(const RelInfo *rel, const DbInfo *db, bool is_new_db)
  *
  * higher level routine to generate dbinfos for the database running
  * on the given "port". Assumes that server is already running.
+ * for check object need check global temp table,
+ * for create object skip global temp table.
  */
 void
-get_db_and_rel_infos(ClusterInfo *cluster)
+get_db_and_rel_infos(ClusterInfo *cluster, bool skip_global_temp)
 {
 	int			dbnum;
 
@@ -283,7 +285,7 @@ get_db_and_rel_infos(ClusterInfo *cluster)
 	get_db_infos(cluster);
 
 	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-		get_rel_infos(cluster, &cluster->dbarr.dbs[dbnum]);
+		get_rel_infos(cluster, &cluster->dbarr.dbs[dbnum], skip_global_temp);
 
 	if (cluster == &old_cluster)
 		pg_log(PG_VERBOSE, "\nsource databases:\n");
@@ -368,7 +370,7 @@ get_db_infos(ClusterInfo *cluster)
  * This allows later processing to match up old and new databases efficiently.
  */
 static void
-get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
+get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo, bool skip_global_temp)
 {
 	PGconn	   *conn = connectToServer(cluster,
 									   dbinfo->db_name);
@@ -411,8 +413,17 @@ get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
 			 "  FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n "
 			 "         ON c.relnamespace = n.oid "
 			 "  WHERE relkind IN (" CppAsString2(RELKIND_RELATION) ", "
-			 CppAsString2(RELKIND_MATVIEW) ") AND "
+			 CppAsString2(RELKIND_MATVIEW) ") AND ");
+
+	if (skip_global_temp)
+	{
+		/* exclude global temp tables */
+		snprintf(query + strlen(query), sizeof(query) - strlen(query),
+			"    relpersistence != " CppAsString2(RELPERSISTENCE_GLOBAL_TEMP) " AND ");
+	}
+
 	/* exclude possible orphaned temp tables */
+	snprintf(query + strlen(query), sizeof(query) - strlen(query),
 			 "    ((n.nspname !~ '^pg_temp_' AND "
 			 "      n.nspname !~ '^pg_toast_temp_' AND "
 			 "      n.nspname NOT IN ('pg_catalog', 'information_schema', "
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 07defacd673..438ae344d09 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -411,7 +411,7 @@ create_new_objects(void)
 		set_frozenxids(true);
 
 	/* update new_cluster info now that we have objects in the databases */
-	get_db_and_rel_infos(&new_cluster);
+	get_db_and_rel_infos(&new_cluster, true);
 }
 
 /*
@@ -649,7 +649,10 @@ set_frozenxids(bool minmxid_only)
 									  "UPDATE	pg_catalog.pg_class "
 									  "SET	relfrozenxid = '%u' "
 			/* only heap, materialized view, and TOAST are vacuumed */
-									  "WHERE	relkind IN ("
+									  "WHERE "
+			/* exclude global temp tables */
+									  " relpersistence != " CppAsString2(RELPERSISTENCE_GLOBAL_TEMP) " AND "
+									  "relkind IN ("
 									  CppAsString2(RELKIND_RELATION) ", "
 									  CppAsString2(RELKIND_MATVIEW) ", "
 									  CppAsString2(RELKIND_TOASTVALUE) ")",
@@ -660,7 +663,10 @@ set_frozenxids(bool minmxid_only)
 								  "UPDATE	pg_catalog.pg_class "
 								  "SET	relminmxid = '%u' "
 		/* only heap, materialized view, and TOAST are vacuumed */
-								  "WHERE	relkind IN ("
+								  "WHERE "
+		/* exclude global temp tables */
+								  " relpersistence != " CppAsString2(RELPERSISTENCE_GLOBAL_TEMP) " AND "
+								  "relkind IN ("
 								  CppAsString2(RELKIND_RELATION) ", "
 								  CppAsString2(RELKIND_MATVIEW) ", "
 								  CppAsString2(RELKIND_TOASTVALUE) ")",
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index da6770d0f83..2da01c28f37 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -372,7 +372,7 @@ void		check_loadable_libraries(void);
 FileNameMap *gen_db_file_maps(DbInfo *old_db,
 							  DbInfo *new_db, int *nmaps, const char *old_pgdata,
 							  const char *new_pgdata);
-void		get_db_and_rel_infos(ClusterInfo *cluster);
+void		get_db_and_rel_infos(ClusterInfo *cluster, bool skip_global_temp);
 
 /* option.c */
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 40433e32fa0..d1bbab6029b 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3769,7 +3769,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 		 * Show whether a relation is permanent, temporary, or unlogged.
 		 */
 		appendPQExpBuffer(&buf,
-						  ",\n  CASE c.relpersistence WHEN 'p' THEN '%s' WHEN 't' THEN '%s' WHEN 'u' THEN '%s' END as \"%s\"",
+						  ",\n  CASE c.relpersistence WHEN 'g' THEN '%s' WHEN 'p' THEN '%s' WHEN 't' THEN '%s' WHEN 'u' THEN '%s' END as \"%s\"",
+						  gettext_noop("session"),
 						  gettext_noop("permanent"),
 						  gettext_noop("temporary"),
 						  gettext_noop("unlogged"),
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 6bd33a06cb1..d65daa3855b 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1103,6 +1103,8 @@ static const pgsql_thing_t words_after_create[] = {
 	{"FOREIGN TABLE", NULL, NULL, NULL},
 	{"FUNCTION", NULL, NULL, Query_for_list_of_functions},
 	{"GROUP", Query_for_list_of_roles},
+	{"GLOBAL", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE GLOBAL TEMP/TEMPORARY TABLE
+																	* ... */
 	{"INDEX", NULL, NULL, &Query_for_list_of_indexes},
 	{"LANGUAGE", Query_for_list_of_languages},
 	{"LARGE OBJECT", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
@@ -2670,6 +2672,9 @@ psql_completion(const char *text, int start, int end)
 	/* CREATE FOREIGN DATA WRAPPER */
 	else if (Matches("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny))
 		COMPLETE_WITH("HANDLER", "VALIDATOR", "OPTIONS");
+	/* CREATE GLOBAL TEMP/TEMPORARY*/
+	else if (Matches("CREATE", "GLOBAL"))
+		COMPLETE_WITH("TEMP", "TEMPORARY");
 
 	/* CREATE FOREIGN TABLE */
 	else if (Matches("CREATE", "FOREIGN", "TABLE", MatchAny))
@@ -2904,6 +2909,8 @@ psql_completion(const char *text, int start, int end)
 	/* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
 	else if (TailMatches("CREATE", "TEMP|TEMPORARY"))
 		COMPLETE_WITH("SEQUENCE", "TABLE", "VIEW");
+	else if (TailMatches("CREATE", "GLOBAL", "TEMP|TEMPORARY"))
+		COMPLETE_WITH("TABLE", "SEQUENCE");
 	/* Complete "CREATE UNLOGGED" with TABLE or MATVIEW */
 	else if (TailMatches("CREATE", "UNLOGGED"))
 		COMPLETE_WITH("TABLE", "MATERIALIZED VIEW");
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index c4757bda2d5..d99454c984f 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -86,9 +86,9 @@ extern Oid	heap_create_with_catalog(const char *relname,
 
 extern void heap_drop_with_catalog(Oid relid);
 
-extern void heap_truncate(List *relids);
+extern void heap_truncate(List *relids, bool is_global_temp);
 
-extern void heap_truncate_one_rel(Relation rel);
+extern void heap_truncate_one_rel(Relation rel, LOCKMODE lockmode);
 
 extern void heap_truncate_check_FKs(List *relations, bool tempTables);
 
@@ -161,5 +161,5 @@ extern void StorePartitionKey(Relation rel,
 extern void RemovePartitionKeyByRelId(Oid relid);
 extern void StorePartitionBound(Relation rel, Relation parent,
 								PartitionBoundSpec *bound);
-
+extern void CheckGlobalTempTableNotInUse(Relation rel, const char *stmt);
 #endif							/* HEAP_H */
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index a1d6e3b645f..441cd440493 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -158,7 +158,7 @@ extern void reindex_index(Oid indexId, bool skip_constraint_checks,
 #define REINDEX_REL_FORCE_INDEXES_UNLOGGED	0x08
 #define REINDEX_REL_FORCE_INDEXES_PERMANENT 0x10
 
-extern bool reindex_relation(Oid relid, int flags, ReindexParams *params);
+extern bool reindex_relation(Oid relid, int flags, ReindexParams *params, LOCKMODE lockmode);
 
 extern bool ReindexIsProcessingHeap(Oid heapOid);
 extern bool ReindexIsProcessingIndex(Oid indexOid);
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 304e8c18d52..e23cb49c010 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -172,6 +172,7 @@ DECLARE_INDEX(pg_class_tblspc_relfilenode_index, 3455, ClassTblspcRelfilenodeInd
 #define		  RELPERSISTENCE_PERMANENT	'p' /* regular table */
 #define		  RELPERSISTENCE_UNLOGGED	'u' /* unlogged permanent table */
 #define		  RELPERSISTENCE_TEMP		't' /* temporary table */
+#define		  RELPERSISTENCE_GLOBAL_TEMP	'g' /* global temporary table */
 
 /* default selection for replica identity (primary key or nothing) */
 #define		  REPLICA_IDENTITY_DEFAULT	'd'
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0859dc81cac..60d92e2096f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5729,6 +5729,40 @@
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_xact_function_self_time' },
 
+# For global temporary table
+{ oid => '9874',
+  descr => 'List local statistics for global temporary table',
+  proname => 'pg_get_gtt_statistics', provolatile => 'v', proparallel => 'u',
+  prorettype => 'record', proretset => 't', prorows => '10', proargtypes => 'oid int4 anyelement',
+  proallargtypes => '{oid,int4,anyelement,oid,int2,bool,float4,int4,float4,int2,int2,int2,int2,int2,oid,oid,oid,oid,oid,oid,oid,oid,oid,oid,_float4,_float4,_float4,_float4,_float4,anyarray,anyarray,anyarray,anyarray,anyarray}',
+  proargmodes => '{i,i,i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{relid,att,x,starelid,staattnum,stainherit,stanullfrac,stawidth,stadistinct,stakind1,stakind2,stakind3,stakind4,stakind5,staop1,staop2,staop3,staop4,staop5,stacoll1,stacoll2,stacoll3,stacoll4,stacoll5,stanumbers1,stanumbers2,stanumbers3,stanumbers4,stanumbers5,stavalues1,stavalues2,stavalues3,stavalues4,stavalues5}',
+  prosrc => 'pg_get_gtt_statistics' },
+{ oid => '9875',
+  descr => 'List local relstats for global temporary table',
+  proname => 'pg_get_gtt_relstats', provolatile => 'v', proparallel => 'u',
+  prorettype => 'record', proretset => 't', prorows => '10', proargtypes => 'oid',
+  proallargtypes => '{oid,oid,int4,float4,int4,xid,xid}',
+  proargmodes => '{i,o,o,o,o,o,o}',
+  proargnames => '{relid,relfilenode,relpages,reltuples,relallvisible,relfrozenxid,relminmxid}',
+  prosrc => 'pg_get_gtt_relstats' },
+{ oid => '9876',
+  descr => 'List attached pid for one global temporary table',
+  proname => 'pg_gtt_attached_pid', provolatile => 'v', proparallel => 'u',
+  prorettype => 'record', proretset => 't', prorows => '10', proargtypes => 'oid',
+  proallargtypes => '{oid,oid,int4}',
+  proargmodes => '{i,o,o}',
+  proargnames => '{relid,relid,pid}',
+  prosrc => 'pg_gtt_attached_pid' },
+{ oid => '9877',
+  descr => 'List those backends that have used global temporary table',
+  proname => 'pg_list_gtt_relfrozenxids', provolatile => 'v', proparallel => 'u',
+  prorettype => 'record', proretset => 't', prorows => '10', proargtypes => '',
+  proallargtypes => '{int4,xid}',
+  proargmodes => '{o,o}',
+  proargnames => '{pid,relfrozenxid}',
+  prosrc => 'pg_list_gtt_relfrozenxids' },
+
 { oid => '3788',
   descr => 'statistics: timestamp of the current statistics snapshot',
   proname => 'pg_stat_get_snapshot_timestamp', provolatile => 's',
diff --git a/src/include/catalog/storage.h b/src/include/catalog/storage.h
index 9ffc7419131..ca5bcaa3f97 100644
--- a/src/include/catalog/storage.h
+++ b/src/include/catalog/storage.h
@@ -22,7 +22,7 @@
 /* GUC variables */
 extern int	wal_skip_threshold;
 
-extern SMgrRelation RelationCreateStorage(RelFileNode rnode, char relpersistence);
+extern SMgrRelation RelationCreateStorage(RelFileNode rnode, char relpersistence, Relation rel);
 extern void RelationDropStorage(Relation rel);
 extern void RelationPreserveStorage(RelFileNode rnode, bool atCommit);
 extern void RelationPreTruncate(Relation rel);
diff --git a/src/include/catalog/storage_gtt.h b/src/include/catalog/storage_gtt.h
new file mode 100644
index 00000000000..50e0570945a
--- /dev/null
+++ b/src/include/catalog/storage_gtt.h
@@ -0,0 +1,46 @@
+/*-------------------------------------------------------------------------
+ *
+ * storage_gtt.h
+ *	  prototypes for functions in backend/catalog/storage_gtt.c
+ *
+ * src/include/catalog/storage_gtt.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STORAGE_GTT_H
+#define STORAGE_GTT_H
+
+#include "access/htup.h"
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+#include "nodes/execnodes.h"
+#include "utils/relcache.h"
+
+extern int		vacuum_gtt_defer_check_age;
+
+extern Size active_gtt_shared_hash_size(void);
+extern void active_gtt_shared_hash_init(void);
+extern void remember_gtt_storage_info(RelFileNode rnode, Relation rel);
+extern void forget_gtt_storage_info(Oid relid, RelFileNode relfilenode, bool isCommit);
+extern bool is_other_backend_use_gtt(Oid relid);
+extern bool gtt_storage_attached(Oid relid);
+extern void up_gtt_att_statistic(Oid reloid, int attnum, bool inh, int natts,
+								TupleDesc tupleDescriptor, Datum *values, bool *isnull);
+extern HeapTuple get_gtt_att_statistic(Oid reloid, int attnum, bool inh);
+extern void release_gtt_statistic_cache(HeapTuple tup);
+extern void gtt_update_relstats(Relation relation, BlockNumber relpages, double reltuples,
+									BlockNumber relallvisible, TransactionId relfrozenxid,
+									TransactionId relminmxid);
+extern bool get_gtt_relstats(Oid relid, BlockNumber *relpages, double *reltuples,
+							BlockNumber *relallvisible, TransactionId *relfrozenxid,
+							TransactionId *relminmxid);
+extern void force_enable_gtt_index(Relation index);
+extern void gtt_correct_index_session_state(Relation index);
+extern void gtt_init_storage(CmdType operation, Relation relation);
+extern Oid gtt_fetch_current_relfilenode(Oid relid);
+extern void index_update_gtt_relstats(Relation rel, bool hasindex, double reltuples, bool isreindex);
+extern void vac_update_gtt_relstats(Relation relation, BlockNumber num_pages, double num_tuples,
+										BlockNumber num_all_visible_pages, bool hasindex, TransactionId frozenxid,
+										MultiXactId minmulti, bool in_outer_xact);
+extern void GlobalTempRelationSetNewRelfilenode(Relation relation);
+#endif							/* STORAGE_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index f7542f2bccb..e8ae0f00531 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -65,5 +65,6 @@ extern void seq_redo(XLogReaderState *rptr);
 extern void seq_desc(StringInfo buf, XLogReaderState *rptr);
 extern const char *seq_identify(uint8 info);
 extern void seq_mask(char *pagedata, BlockNumber blkno);
+extern void gtt_init_sequence(Relation rel);
 
 #endif							/* SEQUENCE_H */
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 5d4037f26e8..28598b06c1e 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -86,7 +86,7 @@ extern void find_composite_type_dependencies(Oid typeOid,
 
 extern void check_of_type(HeapTuple typetuple);
 
-extern void register_on_commit_action(Oid relid, OnCommitAction action);
+extern void register_on_commit_action(Oid relid, OnCommitAction action, bool is_gloal_temp);
 extern void remove_on_commit_action(Oid relid);
 
 extern void PreCommit_on_commit_actions(void);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 344399f6a8a..ae7628d692b 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -575,7 +575,7 @@ exec_rt_fetch(Index rti, EState *estate)
 
 extern Relation ExecGetRangeTableRelation(EState *estate, Index rti);
 extern void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo,
-								   Index rti);
+								   Index rti, CmdType operation);
 
 extern int	executor_errposition(EState *estate, int location);
 
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index 06dc27995ba..197e367f4e4 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -119,5 +119,6 @@ extern const NameData *attnumAttName(Relation rd, int attid);
 extern Oid	attnumTypeId(Relation rd, int attid);
 extern Oid	attnumCollationId(Relation rd, int attid);
 extern bool isQueryUsingTempRelation(Query *query);
+extern bool isQueryUsingGlobalTempRelation(Query *query);
 
 #endif							/* PARSE_RELATION_H */
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 124977cf7e3..9b64389b08a 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -189,6 +189,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_SHARED_TIDBITMAP,
 	LWTRANCHE_PARALLEL_APPEND,
 	LWTRANCHE_PER_XACT_PREDICATE_LIST,
+	LWTRANCHE_GLOBAL_TEMP_TABLE_CTL,
 	LWTRANCHE_FIRST_USER_DEFINED
 }			BuiltinTrancheIds;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index a58888f9e90..1bbf31923c0 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -164,6 +164,8 @@ struct PGPROC
 	Oid			tempNamespaceId;	/* OID of temp schema this backend is
 									 * using */
 
+	TransactionId gtt_frozenxid;	/* session level global temp table relfrozenxid */
+
 	bool		isBackgroundWorker; /* true if background worker. */
 
 	/*
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index e03692053ee..d948c0383b7 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -94,4 +94,6 @@ extern void ProcArraySetReplicationSlotXmin(TransactionId xmin,
 extern void ProcArrayGetReplicationSlotXmin(TransactionId *xmin,
 											TransactionId *catalog_xmin);
 
+extern TransactionId gtt_get_oldest_frozenxids_in_current_database(List **pids, List **xids);
+
 #endif							/* PROCARRAY_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 6bb81707b09..4927edc2dec 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -288,6 +288,9 @@ extern int	tcp_user_timeout;
 extern bool trace_sort;
 #endif
 
+/* global temporary table */
+extern int	max_active_gtt;
+
 /*
  * Functions exported by guc.c
  */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 6da1b220cdc..6455dfb7ae2 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -57,7 +57,7 @@ typedef struct RelationData
 	SMgrRelation rd_smgr;		/* cached file handle, or NULL */
 	int			rd_refcnt;		/* reference count */
 	BackendId	rd_backend;		/* owning backend id, if temporary relation */
-	bool		rd_islocaltemp; /* rel is a temp rel of this session */
+	bool		rd_islocaltemp;	/* rel is a temp rel of this session */
 	bool		rd_isnailed;	/* rel is nailed in cache */
 	bool		rd_isvalid;		/* relcache entry is valid */
 	bool		rd_indexvalid;	/* is rd_indexlist valid? (also rd_pkindex and
@@ -327,6 +327,7 @@ typedef struct StdRdOptions
 	int			parallel_workers;	/* max number of parallel workers */
 	StdRdOptIndexCleanup vacuum_index_cleanup;	/* controls index vacuuming */
 	bool		vacuum_truncate;	/* enables vacuum to truncate a relation */
+	bool		on_commit_delete_rows;	/* global temp table */
 } StdRdOptions;
 
 #define HEAP_MIN_FILLFACTOR			10
@@ -609,11 +610,13 @@ RelationGetSmgr(Relation rel)
  *		True if relation's pages are stored in local buffers.
  */
 #define RelationUsesLocalBuffers(relation) \
-	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP || \
+	 (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 
 /*
  * RELATION_IS_LOCAL
- *		If a rel is either temp or newly created in the current transaction,
+ *		If a rel is either local temp or global temp relation
+ *		or newly created in the current transaction,
  *		it can be assumed to be accessible only to the current backend.
  *		This is typically used to decide that we can skip acquiring locks.
  *
@@ -621,6 +624,7 @@ RelationGetSmgr(Relation rel)
  */
 #define RELATION_IS_LOCAL(relation) \
 	((relation)->rd_islocaltemp || \
+	 (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP || \
 	 (relation)->rd_createSubid != InvalidSubTransactionId)
 
 /*
@@ -633,6 +637,30 @@ RelationGetSmgr(Relation rel)
 	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP && \
 	 !(relation)->rd_islocaltemp)
 
+/*
+ * RELATION_IS_TEMP_ON_CURRENT_SESSION
+ *		Test a rel is either local temp relation of this session
+ *		or global temp relation.
+ */
+#define RELATION_IS_TEMP_ON_CURRENT_SESSION(relation) \
+	((relation)->rd_islocaltemp || \
+	 (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+
+/*
+ * RELATION_IS_TEMP
+ *		Test a rel is local temporary relation or global temporary relation.
+ */
+#define RELATION_IS_TEMP(relation) \
+	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP || \
+	 (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+
+/*
+ * RelpersistenceTsTemp
+ *		Test a relpersistence is local temp relation or global temporary relation.
+ */
+#define RelpersistenceTsTemp(relpersistence) \
+	(relpersistence == RELPERSISTENCE_TEMP || \
+	 relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 
 /*
  * RelationIsScannable
@@ -678,6 +706,19 @@ RelationGetSmgr(Relation rel)
 	 (relation)->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&	\
 	 !IsCatalogRelation(relation))
 
+/* For global temporary table */
+#define RELATION_IS_GLOBAL_TEMP(relation)	((relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+
+/* Get on commit clause value only for global temporary table */
+#define RELATION_GTT_ON_COMMIT_DELETE(relation)	\
+	((relation)->rd_options && \
+	 ((relation)->rd_rel->relkind == RELKIND_RELATION || (relation)->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) && \
+	 (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP ? \
+	 ((StdRdOptions *) (relation)->rd_options)->on_commit_delete_rows : false)
+
+/* Get relpersistence for relation */
+#define RelationGetRelPersistence(relation) ((relation)->rd_rel->relpersistence)
+
 /* routines in utils/cache/relcache.c */
 extern void RelationIncrementReferenceCount(Relation rel);
 extern void RelationDecrementReferenceCount(Relation rel);
-- 
2.32.0 (Apple Git-132)