diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 8ccc228..b061de9 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -168,6 +168,19 @@ static relopt_bool boolRelOpts[] =
 		},
 		true
 	},
+	/*
+	 * For global temp table only
+	 * use ShareUpdateExclusiveLock for ensure safety
+	 */
+	{
+		{
+			"on_commit_delete_rows",
+			"global temp table on commit options",
+			RELOPT_KIND_HEAP | RELOPT_KIND_PARTITIONED,
+			ShareUpdateExclusiveLock
+		},
+		true
+	},	
 	/* list terminator */
 	{{NULL}}
 };
@@ -1817,6 +1830,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)},
@@ -1961,13 +1976,18 @@ build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
 bytea *
 partitioned_table_reloptions(Datum reloptions, bool validate)
 {
+	static const relopt_parse_elt tab[] = {
+		{"on_commit_delete_rows", RELOPT_TYPE_BOOL,
+		offsetof(StdRdOptions, on_commit_delete_rows)}
+	};
+
 	/*
 	 * There are no options for partitioned tables yet, but this is able to do
 	 * some validation.
 	 */
 	return (bytea *) build_reloptions(reloptions, validate,
 									  RELOPT_KIND_PARTITIONED,
-									  0, NULL, 0);
+									  sizeof(StdRdOptions), tab, lengthof(tab));
 }
 
 /*
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 765329b..4761fdc 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -1011,7 +1011,9 @@ gistproperty(Oid index_oid, int attno,
 XLogRecPtr
 gistGetFakeLSN(Relation rel)
 {
-	if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+	/* global temp is same as local temp table */
+	if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP ||
+		rel->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 	{
 		/*
 		 * 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 3ec6d52..1222594 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -150,7 +150,9 @@ 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)
+	/* global temp table is same as local temp table */
+	if (!(index->rd_rel->relpersistence == RELPERSISTENCE_TEMP ||
+		index->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP))
 		sort_threshold = Min(sort_threshold, NBuffers);
 	else
 		sort_threshold = Min(sort_threshold, NLocBuffer);
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 56b3562..d3443ec 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -587,7 +587,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
@@ -640,7 +640,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(rel->rd_smgr, dstrel, MAIN_FORKNUM,
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 3c18db2..b08a1fc 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -62,6 +62,7 @@
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/storage.h"
+#include "catalog/storage_gtt.h"
 #include "commands/dbcommands.h"
 #include "commands/progress.h"
 #include "commands/vacuum.h"
@@ -431,9 +432,9 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
 	Assert(params->index_cleanup != VACOPT_TERNARY_DEFAULT);
 	Assert(params->truncate != VACOPT_TERNARY_DEFAULT);
 
-	/* not every AM requires these to be valid, but heap does */
-	Assert(TransactionIdIsNormal(onerel->rd_rel->relfrozenxid));
-	Assert(MultiXactIdIsValid(onerel->rd_rel->relminmxid));
+	/* not every AM requires these to be valid, but regular heap does */
+	Assert(RELATION_IS_GLOBAL_TEMP(onerel) ^ TransactionIdIsNormal(onerel->rd_rel->relfrozenxid));
+	Assert(RELATION_IS_GLOBAL_TEMP(onerel) ^ MultiXactIdIsValid(onerel->rd_rel->relminmxid));
 
 	/* measure elapsed time iff autovacuum logging requires it */
 	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 39b8f17..abb76dc 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"
@@ -601,6 +602,10 @@ _bt_getrootheight(Relation rel)
 	{
 		Buffer		metabuf;
 
+		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/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 11e3273..32694c9 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6422,6 +6422,10 @@ StartupXLOG(void)
 	else
 		recoveryTargetTLI = ControlFile->checkPointCopy.ThisTimeLineID;
 
+	/* clean temp relation files */
+	if (max_active_gtt > 0)
+		RemovePgTempFiles();
+
 	/*
 	 * Check for signal files, and if so set up state for offline recovery
 	 */
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 5eaca27..e5002aa 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -231,7 +231,8 @@ Boot_CreateStmt:
 												   mapped_relation,
 												   true,
 												   &relfrozenxid,
-												   &relminmxid);
+												   &relminmxid,
+												   false);
 						elog(DEBUG4, "bootstrap relation created");
 					}
 					else
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 9499bb3..ae47364 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -43,6 +43,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 7d6acae..3be8d63 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -393,7 +393,9 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
 
 	switch (relpersistence)
 	{
+		/* global temp table is same as local temp table */
 		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 632c058..9511182 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -61,6 +61,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"
@@ -99,6 +100,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 								Oid reloftype,
 								Oid relowner,
 								char relkind,
+								char relpersistence,
 								TransactionId relfrozenxid,
 								TransactionId relminmxid,
 								Datum relacl,
@@ -304,7 +306,8 @@ heap_create(const char *relname,
 			bool mapped_relation,
 			bool allow_system_table_mods,
 			TransactionId *relfrozenxid,
-			MultiXactId *relminmxid)
+			MultiXactId *relminmxid,
+			bool skip_create_storage)
 {
 	bool		create_storage;
 	Relation	rel;
@@ -404,6 +407,9 @@ heap_create(const char *relname,
 									 relpersistence,
 									 relkind);
 
+	if (skip_create_storage)
+		create_storage = false;
+
 	/*
 	 * Have the storage manager create the relation's disk file, if needed.
 	 *
@@ -427,7 +433,7 @@ heap_create(const char *relname,
 
 			case RELKIND_INDEX:
 			case RELKIND_SEQUENCE:
-				RelationCreateStorage(rel->rd_node, relpersistence);
+				RelationCreateStorage(rel->rd_node, relpersistence, rel);
 				break;
 
 			case RELKIND_RELATION:
@@ -959,6 +965,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					char relpersistence,
 					TransactionId relfrozenxid,
 					TransactionId relminmxid,
 					Datum relacl,
@@ -997,8 +1004,18 @@ AddNewRelationTuple(Relation pg_class_desc,
 			break;
 	}
 
-	new_rel_reltup->relfrozenxid = relfrozenxid;
-	new_rel_reltup->relminmxid = relminmxid;
+	/* global temp table not remember transaction info in catalog */
+	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;
@@ -1261,7 +1278,8 @@ heap_create_with_catalog(const char *relname,
 							   mapped_relation,
 							   allow_system_table_mods,
 							   &relfrozenxid,
-							   &relminmxid);
+							   &relminmxid,
+							   false);
 
 	Assert(relid == RelationGetRelid(new_rel_desc));
 
@@ -1360,6 +1378,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relpersistence,
 						relfrozenxid,
 						relminmxid,
 						PointerGetDatum(relacl),
@@ -1940,6 +1959,14 @@ heap_drop_with_catalog(Oid relid)
 	if (relid == defaultPartOid)
 		update_default_partition_oid(parentOid, InvalidOid);
 
+	/* We allow to drop global temp table only this session use it */
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+	{
+		if (is_other_backend_use_gtt(RelationGetRelid(rel)))
+			elog(ERROR, "can not drop relation %s when other backend attached this global temp table",
+						RelationGetRelationName(rel));
+	}
+
 	/*
 	 * Schedule unlinking of the relation's physical files at commit.
 	 */
@@ -3167,7 +3194,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;
 
@@ -3179,7 +3206,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
@@ -3225,8 +3252,13 @@ heap_truncate(List *relids)
 	{
 		Oid			rid = lfirst_oid(cell);
 		Relation	rel;
+		LOCKMODE	lockmode = AccessExclusiveLock;
+
+		/* truncate global temp table only need RowExclusiveLock */
+		if (get_rel_persistence(rid) == RELPERSISTENCE_GLOBAL_TEMP)
+			lockmode = RowExclusiveLock;
 
-		rel = table_open(rid, AccessExclusiveLock);
+		rel = table_open(rid, lockmode);
 		relations = lappend(relations, rel);
 	}
 
@@ -3259,6 +3291,7 @@ void
 heap_truncate_one_rel(Relation rel)
 {
 	Oid			toastrelid;
+	LOCKMODE	lockmode = AccessExclusiveLock;
 
 	/*
 	 * Truncate the relation.  Partitioned tables have no storage, so there is
@@ -3267,23 +3300,37 @@ heap_truncate_one_rel(Relation rel)
 	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 		return;
 
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+	{
+		if (!gtt_storage_attached(RelationGetRelid(rel)))
+			return;
+
+		/*
+		 * Truncate global temp table only need RowExclusiveLock
+		 */
+		lockmode = RowExclusiveLock;
+	}
+
 	/* Truncate the underlying relation */
 	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);
 	}
+
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+		up_gtt_relstats(rel, 0, 0, 0, RecentXmin, InvalidMultiXactId);
 }
 
 /*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index bd7ec92..2003115 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -53,6 +53,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"
@@ -728,6 +729,11 @@ index_create(Relation heapRelation,
 	char		relkind;
 	TransactionId relfrozenxid;
 	MultiXactId relminmxid;
+	bool		skip_create_storage = false;
+
+	if (RELATION_IS_GLOBAL_TEMP(heapRelation) &&
+		!gtt_storage_attached(RelationGetRelid(heapRelation)))
+		skip_create_storage = true;
 
 	/* constraint flags can only be set when a constraint is requested */
 	Assert((constr_flags == 0) ||
@@ -881,6 +887,19 @@ index_create(Relation heapRelation,
 						indexRelationName, RelationGetRelationName(heapRelation))));
 	}
 
+	if (RELATION_IS_GLOBAL_TEMP(heapRelation))
+	{
+		/* No support create index on global temp table use concurrent mode yet */
+		if (concurrent)
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("cannot reindex global temporary tables concurrently")));
+
+		/* if global temp table not init storage, then skip build index */
+		if (!gtt_storage_attached(RelationGetRelid(heapRelation)))
+			flags |= INDEX_CREATE_SKIP_BUILD;
+	}
+
 	/*
 	 * construct tuple descriptor for index tuples
 	 */
@@ -935,7 +954,8 @@ index_create(Relation heapRelation,
 								mapped_relation,
 								allow_system_table_mods,
 								&relfrozenxid,
-								&relminmxid);
+								&relminmxid,
+								skip_create_storage);
 
 	Assert(relfrozenxid == InvalidTransactionId);
 	Assert(relminmxid == InvalidMultiXactId);
@@ -2041,7 +2061,8 @@ 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(!(get_rel_persistence(indexId) == RELPERSISTENCE_TEMP ||
+			 get_rel_persistence(indexId) == RELPERSISTENCE_GLOBAL_TEMP) ||
 		   (!concurrent && !concurrent_lock_mode));
 
 	/*
@@ -2073,6 +2094,14 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode)
 	 */
 	CheckTableNotInUse(userIndexRelation, "DROP INDEX");
 
+	/* We allow to drop index on global temp table only this session use it */
+	if (RELATION_IS_GLOBAL_TEMP(userHeapRelation))
+	{
+		if (is_other_backend_use_gtt(RelationGetRelid(userHeapRelation)))
+			elog(ERROR, "can not drop index %s when other backend attached this global temp table.",
+						RelationGetRelationName(userHeapRelation));
+	}
+
 	/*
 	 * Drop Index Concurrently is more or less the reverse process of Create
 	 * Index Concurrently.
@@ -2681,6 +2710,11 @@ index_update_stats(Relation rel,
 	HeapTuple	tuple;
 	Form_pg_class rd_rel;
 	bool		dirty;
+	bool		is_gtt = false;
+
+	/* update index stats into localhash and rel_rd_rel for global temp table */
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+		is_gtt = true;
 
 	/*
 	 * We always update the pg_class row using a non-transactional,
@@ -2766,21 +2800,35 @@ index_update_stats(Relation rel,
 		else					/* don't bother for indexes */
 			relallvisible = 0;
 
-		if (rd_rel->relpages != (int32) relpages)
+		if (is_gtt)
+			rel->rd_rel->relpages = (int32) relpages;
+		else if (rd_rel->relpages != (int32) relpages)
 		{
 			rd_rel->relpages = (int32) relpages;
 			dirty = true;
 		}
-		if (rd_rel->reltuples != (float4) reltuples)
+
+		if (is_gtt)
+			rel->rd_rel->reltuples = (float4) reltuples;
+		else if (rd_rel->reltuples != (float4) reltuples)
 		{
 			rd_rel->reltuples = (float4) reltuples;
 			dirty = true;
 		}
-		if (rd_rel->relallvisible != (int32) relallvisible)
+
+		if (is_gtt)
+			rel->rd_rel->relallvisible = (int32) relallvisible;
+		else if (rd_rel->relallvisible != (int32) relallvisible)
 		{
 			rd_rel->relallvisible = (int32) relallvisible;
 			dirty = true;
 		}
+
+		if (is_gtt)
+		{
+			up_gtt_relstats(rel, relpages, reltuples, relallvisible,
+							InvalidTransactionId, InvalidMultiXactId);
+		}
 	}
 
 	/*
@@ -2894,6 +2942,15 @@ index_build(Relation heapRelation,
 		pgstat_progress_update_multi_param(6, index, val);
 	}
 
+	if (RELATION_IS_GLOBAL_TEMP(indexRelation))
+	{
+		if (!gtt_storage_attached(RelationGetRelid(indexRelation)))
+		{
+			gtt_force_enable_index(indexRelation);
+			RelationCreateStorage(indexRelation->rd_node, RELPERSISTENCE_GLOBAL_TEMP, indexRelation);
+		}
+	}
+
 	/*
 	 * Call the access method's build procedure
 	 */
@@ -3442,6 +3499,10 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
 	PGRUsage	ru0;
 	bool		progress = (options & REINDEXOPT_REPORT_PROGRESS) != 0;
 
+	if (persistence == RELPERSISTENCE_GLOBAL_TEMP &&
+		!gtt_storage_attached(indexId))
+		return;
+
 	pg_rusage_init(&ru0);
 
 	/*
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 2ec2301..1b6061d 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -655,6 +655,13 @@ RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid)
 							 errmsg("cannot create temporary relation in non-temporary schema")));
 			}
 			break;
+		/* global temp table */
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			if (isAnyTempNamespace(nspid))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("cannot create global temp relations in temporary schemas")));
+			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 d713d5c..afcf004 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -27,6 +27,7 @@
 #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"
@@ -61,6 +62,7 @@ typedef struct PendingRelDelete
 {
 	RelFileNode relnode;		/* relation that may need to be deleted */
 	BackendId	backend;		/* InvalidBackendId if not a temp rel */
+	Oid			relOid;			/* InvalidOid if not a global temp 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 +117,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;
@@ -127,6 +129,8 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 	switch (relpersistence)
 	{
 		case RELPERSISTENCE_TEMP:
+		/* global temp table use same storage strategy as local temp table */
+		case RELPERSISTENCE_GLOBAL_TEMP:
 			backend = BackendIdForTempRelations();
 			needs_wal = false;
 			break;
@@ -154,6 +158,7 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 		MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
 	pending->relnode = rnode;
 	pending->backend = backend;
+	pending->relOid = InvalidOid;
 	pending->atCommit = false;	/* delete if abort */
 	pending->nestLevel = GetCurrentTransactionNestLevel();
 	pending->next = pendingDeletes;
@@ -165,6 +170,13 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 		AddPendingSync(&rnode);
 	}
 
+	/* remember global temp table storage info to localhash */
+	if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP && rel)
+	{
+		pending->relOid = RelationGetRelid(rel);
+		remember_gtt_storage_info(rnode, rel);
+	}
+
 	return srel;
 }
 
@@ -201,11 +213,15 @@ RelationDropStorage(Relation rel)
 		MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
 	pending->relnode = rel->rd_node;
 	pending->backend = rel->rd_backend;
+	pending->relOid = InvalidOid;
 	pending->atCommit = true;	/* delete if commit */
 	pending->nestLevel = GetCurrentTransactionNestLevel();
 	pending->next = pendingDeletes;
 	pendingDeletes = pending;
 
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+		pending->relOid = 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
@@ -602,6 +618,7 @@ smgrDoPendingDeletes(bool isCommit)
 				i = 0,
 				maxrels = 0;
 	SMgrRelation *srels = NULL;
+	Oid			*reloids = NULL;
 
 	prev = NULL;
 	for (pending = pendingDeletes; pending != NULL; pending = next)
@@ -631,14 +648,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->relOid;
+				nrels++;
 			}
 			/* must explicitly free the list entry */
 			pfree(pending);
@@ -651,9 +672,18 @@ smgrDoPendingDeletes(bool isCommit)
 		smgrdounlinkall(srels, nrels, false);
 
 		for (i = 0; i < nrels; i++)
+		{
 			smgrclose(srels[i]);
 
+			/* clean global temp table flags when transaction commit or rollback */
+			if (SmgrIsTemp(srels[i]) &&
+				reloids[i] != InvalidOid &&
+				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 0000000..451cf82
--- /dev/null
+++ b/src/backend/catalog/storage_gtt.c
@@ -0,0 +1,1491 @@
+/*-------------------------------------------------------------------------
+ *
+ * storage_gtt.c
+ *	  code to create and destroy physical storage for global temparary table
+ *
+ * IDENTIFICATION
+ *	  src/backend/catalog/storage_gtt.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/amapi.h"
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/table.h"
+#include "access/relation.h"
+#include "access/visibilitymap.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xloginsert.h"
+#include "access/xlogutils.h"
+#include "catalog/storage.h"
+#include "catalog/storage_xlog.h"
+#include "catalog/storage_gtt.h"
+#include "catalog/heap.h"
+#include "catalog/namespace.h"
+#include "catalog/index.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_statistic.h"
+#include "commands/tablecmds.h"
+#include "commands/sequence.h"
+#include "funcapi.h"
+#include "nodes/primnodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/execnodes.h"
+#include "miscadmin.h"
+#include "storage/freespace.h"
+#include "storage/smgr.h"
+#include "storage/ipc.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "storage/sinvaladt.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/hsearch.h"
+#include "utils/catcache.h"
+#include "utils/lsyscache.h"
+#include <utils/relcache.h>
+#include "utils/inval.h"
+#include "utils/guc.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_relstats_context = NULL;
+
+/* relfrozenxid of all gtts in the current session */
+static List *gtt_session_relfrozenxid_list = NIL;
+static TransactionId gtt_session_frozenxid = InvalidTransactionId;
+
+int		vacuum_gtt_defer_check_age = 0;
+
+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;
+
+typedef struct
+{
+	gtt_fnode	rnode;
+	Bitmapset	*map;
+	/* bitmap data */
+} gtt_shared_hash_entry;
+
+typedef struct
+{
+	Oid			relfilenode;
+	Oid			spcnode;
+
+	/* pg_class stat */
+	int32		relpages;
+	float4		reltuples;
+	int32		relallvisible;
+	TransactionId relfrozenxid;
+	TransactionId relminmxid;
+} gtt_relfilenode;
+
+typedef struct
+{
+	Oid			relid;
+
+	List		*relfilenode_list;
+
+	char		relkind;
+	bool		on_commit_delete;
+
+	/* pg_statistic */
+	int			natts;
+	int			*attnum;
+	HeapTuple	*att_stat_tups;
+
+	Oid			oldrelid;			/* remember the source of relid, before the switch relfilenode. */
+} 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 skiplock, 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_reset_statistics(gtt_local_hash_entry *entry);
+static void gtt_free_statistics(gtt_local_hash_entry *entry);
+static gtt_relfilenode *gtt_search_relfilenode(gtt_local_hash_entry	*entry, Oid relfilenode, bool missing_ok);
+static gtt_local_hash_entry *gtt_search_by_relid(Oid relid, bool missing_ok);
+
+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);
+
+
+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_size += MAXALIGN(sizeof(gtt_shared_hash_entry));
+	hash_entry_size += MAXALIGN(BITMAPSET_SIZE(wordnum + 1));
+
+	return hash_entry_size;
+}
+
+Size
+active_gtt_shared_hash_size(void)
+{
+	Size	size = 0;
+	Size	hash_entry_size = 0;
+
+	if (max_active_gtt <= 0)
+		return 0;
+
+	size = MAXALIGN(sizeof(gtt_ctl_data));
+	hash_entry_size = action_gtt_shared_hash_entry_size();
+	size += hash_estimate_size(max_active_gtt, hash_entry_size);
+
+	return size;
+}
+
+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_GTT_CTL, "gtt_shared_ctl");
+		LWLockInitialize(&gtt_shared_ctl->lock, LWTRANCHE_GTT_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);
+}
+
+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 == false)
+	{
+		int			wordnum;
+
+		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;
+	}
+
+	bms_add_member(entry->map, MyBackendId);
+	LWLockRelease(&gtt_shared_ctl->lock);
+}
+
+static void
+gtt_storage_checkout(Oid relid, bool skiplock, bool isCommit)
+{
+	gtt_shared_hash_entry	*entry;
+	gtt_fnode				fnode;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	fnode.dbNode = MyDatabaseId;
+	fnode.relNode = relid;
+	if (!skiplock)
+		LWLockAcquire(&gtt_shared_ctl->lock, LW_EXCLUSIVE);
+
+	entry = hash_search(active_gtt_shared_hash,
+					(void *) &(fnode), HASH_FIND, NULL);
+
+	if (entry == NULL)
+	{
+		if (!skiplock)
+			LWLockRelease(&gtt_shared_ctl->lock);
+
+		if (isCommit)
+			elog(WARNING, "relid %u not exist in gtt shared hash when forget", relid);
+
+		return;
+	}
+
+	Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
+	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");
+	}
+
+	if (!skiplock)
+		LWLockRelease(&gtt_shared_ctl->lock);
+
+	return;
+}
+
+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 == NULL)
+	{
+		LWLockRelease(&gtt_shared_ctl->lock);
+		return NULL;
+	}
+
+	Assert(entry->map);
+	if (!bms_is_empty(entry->map))
+		map_copy = bms_copy(entry->map);
+
+	LWLockRelease(&gtt_shared_ctl->lock);
+
+	return map_copy;
+}
+
+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);
+
+	num_use = bms_num_members(entry->map);
+	if (num_use == 0)
+		in_use = false;
+	else if (num_use == 1)
+	{
+		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;
+}
+
+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);
+
+	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));
+
+	if (gtt_storage_local_hash == NULL)
+	{
+#define GTT_LOCAL_HASH_SIZE		1024
+		/* First time through: initialize the hash table */
+		HASHCTL		ctl;
+
+		MemSet(&ctl, 0, sizeof(ctl));
+		ctl.keysize = sizeof(Oid);
+		ctl.entrysize = sizeof(gtt_local_hash_entry);
+		gtt_storage_local_hash =
+			hash_create("global temporary table info",
+						GTT_LOCAL_HASH_SIZE,
+						&ctl, HASH_ELEM | HASH_BLOBS);
+
+		if (!CacheMemoryContext)
+			CreateCacheMemoryContext();
+
+		gtt_relstats_context =
+			AllocSetContextCreate(CacheMemoryContext,
+							"gtt relstats context",
+							ALLOCSET_DEFAULT_SIZES);
+	}
+
+	oldcontext = MemoryContextSwitchTo(gtt_relstats_context);
+
+	entry = gtt_search_by_relid(relid, true);
+	if (!entry)
+	{
+		bool		found = false;
+		int 		natts = 0;
+
+		/* 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;
+		entry->natts = 0;
+		entry->attnum = NULL;
+		entry->att_stat_tups = NULL;
+		entry->oldrelid = InvalidOid;
+
+		natts = RelationGetNumberOfAttributes(rel);
+		entry->attnum = palloc0(sizeof(int) * natts);
+		entry->att_stat_tups = palloc0(sizeof(HeapTuple) * natts);
+		entry->natts = natts;
+
+		if (entry->relkind == RELKIND_RELATION)
+		{
+			if (RELATION_GTT_ON_COMMIT_DELETE(rel))
+			{
+				entry->on_commit_delete = true;
+				register_on_commit_action(RelationGetRelid(rel), ONCOMMIT_DELETE_ROWS);
+			}
+		}
+
+		if (entry->relkind == RELKIND_RELATION ||
+			entry->relkind == RELKIND_SEQUENCE)
+		{
+			gtt_storage_checkin(relid);
+		}
+	}
+
+	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;
+	entry->relfilenode_list = lappend(entry->relfilenode_list, new_node);
+
+	/* only heap contain transaction information */
+	if (entry->relkind == RELKIND_RELATION)
+	{
+		new_node->relfrozenxid = RecentXmin;
+		new_node->relminmxid = GetOldestMultiXactId();
+		insert_gtt_relfrozenxid_to_ordered_list(new_node->relfrozenxid);
+		set_gtt_session_relfrozenxid();
+	}
+
+	gtt_reset_statistics(entry);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	if (!gtt_cleaner_exit_registered)
+	{
+		before_shmem_exit(gtt_storage_removeall, 0);
+		gtt_cleaner_exit_registered = true;
+	}
+
+	return;
+}
+
+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, true);
+	if (d_rnode == NULL)
+	{
+		if (isCommit)
+			elog(ERROR,"gtt relfilenode %u not found in rel %u", rnode.relNode, relid);
+		else if (entry->oldrelid != InvalidOid)
+		{
+			gtt_local_hash_entry *entry2 = NULL;
+			gtt_relfilenode *gttnode2 = NULL;
+
+			entry2 = gtt_search_by_relid(entry->oldrelid, false);
+			gttnode2 = gtt_search_relfilenode(entry2, rnode.relNode, false);
+			Assert(gttnode2->relfilenode == rnode.relNode);
+			Assert(list_length(entry->relfilenode_list) == 1);
+			/* rollback switch relfilenode */
+			gtt_switch_rel_relfilenode(entry2->relid, gttnode2->relfilenode,
+									   entry->relid, gtt_fetch_current_relfilenode(entry->relid),
+									   false);
+			/* clean up footprint */
+			entry2->oldrelid = InvalidOid;
+			d_rnode = gtt_search_relfilenode(entry, rnode.relNode, false);
+			Assert(d_rnode);
+		}
+		else
+		{
+			if (entry->relfilenode_list == NIL)
+			{
+				if (entry->relkind == RELKIND_RELATION ||
+					entry->relkind == RELKIND_SEQUENCE)
+					gtt_storage_checkout(relid, false, isCommit);
+
+				gtt_free_statistics(entry);
+				hash_search(gtt_storage_local_hash,
+						(void *) &(relid), HASH_REMOVE, NULL);
+			}
+
+			return;
+		}
+	}
+
+	if (entry->relkind == RELKIND_RELATION)
+	{
+		Assert(TransactionIdIsNormal(d_rnode->relfrozenxid) || !isCommit);
+		if (TransactionIdIsValid(d_rnode->relfrozenxid))
+		{
+			remove_gtt_relfrozenxid_from_ordered_list(d_rnode->relfrozenxid);
+			set_gtt_session_relfrozenxid();
+		}
+	}
+
+	entry->relfilenode_list = list_delete_ptr(entry->relfilenode_list, d_rnode);
+	pfree(d_rnode);
+	if (entry->relfilenode_list == NIL)
+	{
+		if (entry->relkind == RELKIND_RELATION ||
+			entry->relkind == RELKIND_SEQUENCE)
+			gtt_storage_checkout(relid, false, isCommit);
+
+		if (isCommit && entry->oldrelid != InvalidOid)
+		{
+			gtt_local_hash_entry *entry2 = NULL;
+
+			entry2 = gtt_search_by_relid(entry->oldrelid, false);
+			/* clean up footprint */
+			entry2->oldrelid = InvalidOid;
+		}
+
+		gtt_free_statistics(entry);
+		hash_search(gtt_storage_local_hash,
+				(void *) &(relid), HASH_REMOVE, NULL);
+	}
+	else
+		gtt_reset_statistics(entry);
+
+	return;
+}
+
+/* is the storage file was created in this backend */
+bool
+gtt_storage_attached(Oid relid)
+{
+	bool found = false;
+	gtt_local_hash_entry *entry = NULL;
+
+	if (max_active_gtt <= 0)
+		return false;
+
+	entry = gtt_search_by_relid(relid, true);
+	if (entry)
+		found = true;
+
+	return found;
+}
+
+static void
+gtt_storage_removeall(int code, Datum arg)
+{
+	HASH_SEQ_STATUS				status;
+	gtt_local_hash_entry		*entry;
+	SMgrRelation	*srels = NULL;
+	Oid				*relids = NULL;
+	char			*relkinds = NULL;
+	int			nrels = 0,
+				nfiles = 0,
+				maxrels = 0,
+				maxfiles = 0,
+				i = 0;
+
+	if (gtt_storage_local_hash == NULL)
+		return;
+
+	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;
+			RelFileNode 	rnode;
+			gtt_relfilenode *gtt_rnode = lfirst(lc);
+
+			rnode.spcNode = gtt_rnode->spcnode;
+			rnode.dbNode = MyDatabaseId;
+			rnode.relNode = gtt_rnode->relfilenode;
+			srel = smgropen(rnode, MyBackendId);
+
+			if (maxfiles == 0)
+			{
+				maxfiles = 32;
+				srels = palloc(sizeof(SMgrRelation) * maxfiles);
+			}
+			else if (maxfiles <= nfiles)
+			{
+				maxfiles *= 2;
+				srels = repalloc(srels, sizeof(SMgrRelation) * maxfiles);
+			}
+
+			srels[nfiles++] = srel;
+		}
+
+		if (maxrels == 0)
+		{
+			maxrels = 32;
+			relids  = palloc(sizeof(Oid) * maxrels);
+			relkinds = palloc(sizeof(char) * maxrels);
+		}
+		else if (maxrels <= nrels)
+		{
+			maxrels *= 2;
+			relids  = repalloc(relids , sizeof(Oid) * maxrels);
+			relkinds = repalloc(relkinds, sizeof(char) * maxrels);
+		}
+
+		relkinds[nrels] = entry->relkind;
+		relids[nrels] = entry->relid;
+		nrels++;
+	}
+
+	if (nfiles > 0)
+	{
+		smgrdounlinkall(srels, nfiles, false);
+		for (i = 0; i < nfiles; i++)
+			smgrclose(srels[i]);
+
+		pfree(srels);
+	}
+
+	if (nrels)
+	{
+		LWLockAcquire(&gtt_shared_ctl->lock, LW_EXCLUSIVE);
+		for (i = 0; i < nrels; i++)
+		{
+			if (relkinds[i] == RELKIND_RELATION ||
+				relkinds[i] == RELKIND_SEQUENCE)
+				gtt_storage_checkout(relids[i], true, false);
+		}
+		LWLockRelease(&gtt_shared_ctl->lock);
+
+		pfree(relids);
+		pfree(relkinds);
+	}
+
+	MyProc->session_gtt_frozenxid = InvalidTransactionId;
+
+	return;
+}
+
+/*
+ * Update global temp table relstats(relpage/reltuple/relallvisible) 
+ * to local hashtable
+ */
+void
+up_gtt_relstats(Relation relation,
+					BlockNumber num_pages,
+					double num_tuples,
+					BlockNumber num_all_visible_pages,
+					TransactionId relfrozenxid,
+					TransactionId relminmxid)
+{
+	Oid		relid = RelationGetRelid(relation);
+	gtt_local_hash_entry	*entry;
+	gtt_relfilenode			*gtt_rnode = NULL;
+
+	if (max_active_gtt <= 0)
+		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 (num_pages >= 0 &&
+		gtt_rnode->relpages != (int32)num_pages)
+		gtt_rnode->relpages = (int32)num_pages;
+
+	if (num_tuples >= 0 &&
+		gtt_rnode->reltuples != (float4)num_tuples)
+		gtt_rnode->reltuples = (float4)num_tuples;
+
+	/* only heap contain transaction information and relallvisible */
+	if (entry->relkind == RELKIND_RELATION)
+	{
+		if (gtt_rnode->relallvisible >= 0 &&
+			gtt_rnode->relallvisible != (int32)num_all_visible_pages)
+		{
+			gtt_rnode->relallvisible = (int32)num_all_visible_pages;
+		}
+
+		if (TransactionIdIsNormal(relfrozenxid) &&
+			gtt_rnode->relfrozenxid != relfrozenxid &&
+			(TransactionIdPrecedes(gtt_rnode->relfrozenxid, relfrozenxid) ||
+			 TransactionIdPrecedes(ReadNewTransactionId(), gtt_rnode->relfrozenxid)))
+		{
+			remove_gtt_relfrozenxid_from_ordered_list(gtt_rnode->relfrozenxid);
+			gtt_rnode->relfrozenxid = relfrozenxid;
+			insert_gtt_relfrozenxid_to_ordered_list(relfrozenxid);
+			set_gtt_session_relfrozenxid();
+		}
+
+		if (MultiXactIdIsValid(relminmxid) &&
+			gtt_rnode->relminmxid != relminmxid &&
+			(MultiXactIdPrecedes(gtt_rnode->relminmxid, relminmxid) ||
+			 MultiXactIdPrecedes(ReadNextMultiXactId(), gtt_rnode->relminmxid)))
+		{
+			gtt_rnode->relminmxid = relminmxid;
+		}
+	}
+
+	return;
+}
+
+/*
+ * Search global temp table relstats(relpage/reltuple/relallvisible) 
+ * from local hashtable.
+ */
+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 global temp table statistic info(definition is same as pg_statistic)
+ * to local hashtable where ananyze global temp table
+ */
+void
+up_gtt_att_statistic(Oid reloid, int attnum, bool inh, int natts,
+					TupleDesc tupleDescriptor, Datum *values, bool *isnull)
+{
+	gtt_local_hash_entry	*entry;
+	MemoryContext			oldcontext;
+	int						i = 0;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	entry = gtt_search_by_relid(reloid, true);
+	if (entry == NULL)
+		return;
+
+	if (entry->natts < natts)
+	{
+		elog(WARNING, "reloid %u not support update attstat after add colunm", reloid);
+		return;
+	}
+
+	oldcontext = MemoryContextSwitchTo(gtt_relstats_context);
+	Assert(entry->relid == reloid);
+	for (i = 0; i < entry->natts; i++)
+	{
+		if (entry->attnum[i] == 0)
+		{
+			entry->attnum[i] = attnum;
+			break;
+		}
+		else if (entry->attnum[i] == attnum)
+		{
+			Assert(entry->att_stat_tups[i]);
+			heap_freetuple(entry->att_stat_tups[i]);
+			entry->att_stat_tups[i] = NULL;
+			break;
+		}
+	}
+
+	Assert(i < entry->natts);
+	Assert(entry->att_stat_tups[i] == NULL);
+	entry->att_stat_tups[i] = heap_form_tuple(tupleDescriptor, values, isnull);
+	MemoryContextSwitchTo(oldcontext);
+
+	return;
+}
+
+/*
+ * Search global temp table statistic info(definition is same as pg_statistic)
+ * from local hashtable.
+ */
+HeapTuple
+get_gtt_att_statistic(Oid reloid, int attnum, bool inh)
+{
+	gtt_local_hash_entry	*entry;
+	int						i = 0;
+
+	if (max_active_gtt <= 0)
+		return NULL;
+
+	entry = gtt_search_by_relid(reloid, true);
+	if (entry == NULL)
+		return NULL;
+
+	for (i = 0; i < entry->natts; i++)
+	{
+		if (entry->attnum[i] == attnum)
+		{
+			Assert(entry->att_stat_tups[i]);
+			return entry->att_stat_tups[i];
+		}
+	}
+
+	return NULL;
+}
+
+void
+release_gtt_statistic_cache(HeapTuple tup)
+{
+	/* do nothing */
+	return;
+}
+
+static void
+insert_gtt_relfrozenxid_to_ordered_list(Oid relfrozenxid)
+{
+	MemoryContext	oldcontext;
+	ListCell	*cell;
+	int			i;
+
+	Assert(TransactionIdIsNormal(relfrozenxid));
+
+	oldcontext = MemoryContextSwitchTo(gtt_relstats_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;
+}
+
+static void
+remove_gtt_relfrozenxid_from_ordered_list(Oid relfrozenxid)
+{
+	gtt_session_relfrozenxid_list =
+		list_delete_oid(gtt_session_relfrozenxid_list, relfrozenxid);
+}
+
+static void
+set_gtt_session_relfrozenxid(void)
+{
+	TransactionId gtt_frozenxid = InvalidTransactionId;
+
+	if (gtt_session_relfrozenxid_list)
+		gtt_frozenxid = llast_oid(gtt_session_relfrozenxid_list);
+
+	gtt_session_frozenxid = gtt_frozenxid;
+	if (MyProc->session_gtt_frozenxid != gtt_frozenxid)
+		MyProc->session_gtt_frozenxid = gtt_frozenxid;
+}
+
+Datum
+pg_get_gtt_statistics(PG_FUNCTION_ARGS)
+{
+	HeapTuple	tuple;
+	Relation	rel = NULL;
+	int		attnum = PG_GETARG_INT32(1);
+	Oid		reloid = PG_GETARG_OID(0);
+	char		rel_persistence;
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	  tupdesc;
+	MemoryContext oldcontext;
+	Tuplestorestate *tupstore;
+	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);
+	rel_persistence = get_rel_persistence(reloid);
+	if (rel_persistence != RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		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);
+
+	tuple = get_gtt_att_statistic(reloid, attnum, false);
+	if (tuple)
+	{
+		Datum	values[31];
+		bool	isnull[31];
+		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);
+	}
+
+	relation_close(rel, NoLock);
+	relation_close(pg_tatistic, AccessShareLock);
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+Datum
+pg_get_gtt_relstats(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo	*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc		tupdesc;
+	Tuplestorestate	*tupstore;
+	MemoryContext	oldcontext;
+	HeapTuple	tuple;
+	Oid		reloid = PG_GETARG_OID(0);
+	char	rel_persistence;
+	BlockNumber	relpages = 0;
+	double		reltuples = 0;
+	BlockNumber	relallvisible = 0;
+	uint32		relfrozenxid = 0;
+	uint32		relminmxid = 0;
+	Oid			relnode = 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);
+	rel_persistence = get_rel_persistence(reloid);
+	if (rel_persistence != RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		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;
+}
+
+Datum
+pg_gtt_attached_pid(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo	*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext	oldcontext;
+	HeapTuple	tuple;
+	Oid		reloid = PG_GETARG_OID(0);
+	char	rel_persistence;
+	Relation	rel = NULL;
+	PGPROC		*proc = NULL;
+	Bitmapset	*map = NULL;
+	pid_t		pid = 0;
+	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);
+	rel_persistence = get_rel_persistence(reloid);
+	if (rel_persistence != RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		elog(WARNING, "relation OID %u is not a global temporary table", reloid);
+		relation_close(rel, NoLock);
+		return (Datum) 0;
+	}
+
+	map = copy_active_gtt_bitmap(reloid);
+	if (map)
+	{
+		backendid = bms_first_member(map);
+
+		do
+		{
+			proc = BackendIdGetProc(backendid);
+			pid = proc->pid;
+			if (pid > 0)
+			{
+				Datum	values[2];
+				bool	isnull[2];
+
+				memset(isnull, false, 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, NoLock);
+
+	return (Datum) 0;
+}
+
+Datum
+pg_list_gtt_relfrozenxids(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo	*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext	oldcontext;
+	HeapTuple	tuple;
+	int			num_xid = MaxBackends + 1;
+	int			*pids = NULL;
+	uint32		*xids = NULL;
+	int			i = 0;
+	int			j = 0;
+	uint32		oldest = 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);
+
+	if (max_active_gtt <= 0)
+		return (Datum) 0;
+
+	if (RecoveryInProgress())
+		return (Datum) 0;
+
+	pids = palloc0(sizeof(int) * num_xid);
+	xids = palloc0(sizeof(int) * num_xid);
+	oldest = list_all_session_gtt_frozenxids(num_xid, pids, xids, &i);
+	if (i > 0)
+	{
+		if (i > 0)
+		{
+			pids[i] = 0;
+			xids[i] = oldest;
+			i++;
+		}
+
+		for(j = 0; j < i; j++)
+		{
+			Datum	values[2];
+			bool	isnull[2];
+
+			memset(isnull, false, sizeof(isnull));
+			memset(values, 0, sizeof(values));
+			values[0] = Int32GetDatum(pids[j]);
+			values[1] = UInt32GetDatum(xids[j]);
+			tuple = heap_form_tuple(tupdesc, values, isnull);
+			tuplestore_puttuple(tupstore, tuple);
+		}
+	}
+	tuplestore_donestoring(tupstore);
+	pfree(pids);
+	pfree(xids);
+
+	return (Datum) 0;
+}
+
+void
+gtt_force_enable_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;
+}
+
+void
+gtt_fix_index_state(Relation index)
+{
+	Oid indexOid = RelationGetRelid(index);
+	Oid relOid = index->rd_index->indrelid;
+
+	if (!RELATION_IS_GLOBAL_TEMP(index))
+		return;
+
+	if (!index->rd_index->indisvalid)
+		return;
+
+	if (gtt_storage_attached(relOid) &&
+		!gtt_storage_attached(indexOid))
+	{
+		index->rd_index->indisvalid = false;
+		index->rd_index->indislive = false;
+		index->rd_index->indisready = false;
+	}
+
+	return;
+}
+
+void
+init_gtt_storage(CmdType operation, ResultRelInfo *resultRelInfo)
+{
+	Relation	relation = resultRelInfo->ri_RelationDesc;
+	int			i;
+	Oid			toastrelid;
+
+	if (!(operation == CMD_UTILITY || operation == CMD_INSERT))
+		return;
+
+	if (!RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
+		return;
+
+	if (!RELATION_IS_GLOBAL_TEMP(relation))
+		return;
+
+	if (gtt_storage_attached(RelationGetRelid(relation)))
+		return;
+
+	RelationCreateStorage(relation->rd_node, RELPERSISTENCE_GLOBAL_TEMP, relation);
+
+	for (i = 0; i < resultRelInfo->ri_NumIndices; i++)
+	{
+		Relation	index = resultRelInfo->ri_IndexRelationDescs[i];
+		IndexInfo	*info = resultRelInfo->ri_IndexRelationInfo[i];
+
+		Assert(index->rd_index->indisvalid);
+		Assert(index->rd_index->indislive);
+		Assert(index->rd_index->indisready);
+
+		index_build(relation, index, info, true, false);
+	}
+
+	toastrelid = relation->rd_rel->reltoastrelid;
+	if (OidIsValid(toastrelid))
+	{
+		Relation	toastrel;
+		ListCell	*indlist;
+
+		toastrel = table_open(toastrelid, RowExclusiveLock);
+		RelationCreateStorage(toastrel->rd_node, RELPERSISTENCE_GLOBAL_TEMP, toastrel);
+
+		foreach(indlist, RelationGetIndexList(toastrel))
+		{
+			Oid 		indexId = lfirst_oid(indlist);
+			Relation	currentIndex;
+			IndexInfo 	*indexInfo;
+
+			currentIndex = index_open(indexId, RowExclusiveLock);
+
+			indexInfo = BuildDummyIndexInfo(currentIndex);
+			index_build(toastrel, currentIndex, indexInfo, true, false);
+			index_close(currentIndex, NoLock);
+		}
+
+		table_close(toastrel, NoLock);
+	}
+
+	return;
+}
+
+static void
+gtt_reset_statistics(gtt_local_hash_entry *entry)
+{
+	int i;
+
+	for (i = 0; i < entry->natts; i++)
+	{
+		if (entry->att_stat_tups[i])
+		{
+			heap_freetuple(entry->att_stat_tups[i]);
+			entry->att_stat_tups[i] = NULL;
+		}
+
+		entry->attnum[i] = 0;
+	}
+
+	return;
+}
+
+static void
+gtt_free_statistics(gtt_local_hash_entry *entry)
+{
+	int i;
+
+	for (i = 0; i < entry->natts; i++)
+	{
+		if (entry->att_stat_tups[i])
+		{
+			heap_freetuple(entry->att_stat_tups[i]);
+			entry->att_stat_tups[i] = NULL;
+		}
+	}
+
+	if (entry->attnum)
+		pfree(entry->attnum);
+
+	if (entry->att_stat_tups)
+		pfree(entry->att_stat_tups);
+
+	return;
+}
+
+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;
+}
+
+void
+gtt_switch_rel_relfilenode(Oid rel1, Oid relfilenode1, Oid rel2, Oid relfilenode2, bool footprint)
+{
+	gtt_local_hash_entry	*entry1;
+	gtt_local_hash_entry	*entry2;
+	gtt_relfilenode			*gtt_rnode1 = NULL;
+	gtt_relfilenode			*gtt_rnode2 = NULL;
+	MemoryContext			oldcontext;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	if (gtt_storage_local_hash == NULL)
+		return;
+
+	entry1 = gtt_search_by_relid(rel1, false);
+	gtt_rnode1 = gtt_search_relfilenode(entry1, relfilenode1, false);
+
+	entry2 = gtt_search_by_relid(rel2, false);
+	gtt_rnode2 = gtt_search_relfilenode(entry2, relfilenode2, false);
+
+	oldcontext = MemoryContextSwitchTo(gtt_relstats_context);
+	entry1->relfilenode_list = list_delete_ptr(entry1->relfilenode_list, gtt_rnode1);
+	entry2->relfilenode_list = lappend(entry2->relfilenode_list, gtt_rnode1);
+
+	entry2->relfilenode_list = list_delete_ptr(entry2->relfilenode_list, gtt_rnode2);
+	entry1->relfilenode_list = lappend(entry1->relfilenode_list, gtt_rnode2);
+	MemoryContextSwitchTo(oldcontext);
+
+	if (footprint)
+	{
+		entry1->oldrelid = rel2;
+		entry2->oldrelid = rel1;
+	}
+
+	return;
+}
+
+static gtt_relfilenode *
+gtt_search_relfilenode(gtt_local_hash_entry	*entry, Oid relfilenode, 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)
+		{
+			rnode = gtt_rnode;
+			break;
+		}
+	}
+
+	if (!missing_ok && rnode == NULL)
+		elog(ERROR, "find relfilenode %u relfilenodelist from relid %u fail", relfilenode, entry->relid);
+
+	return rnode;
+}
+
+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;
+}
+
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index d406ea8..88be041 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -186,6 +186,91 @@ CREATE OR REPLACE 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 924ef37..db12eef 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"
@@ -103,7 +104,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);
 
@@ -184,6 +185,13 @@ analyze_rel(Oid relid, RangeVar *relation,
 		return;
 	}
 
+	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
 	 */
@@ -586,14 +594,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]));
 		}
 
 		/*
@@ -1456,7 +1465,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;
@@ -1558,31 +1567,45 @@ 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))
+		/* Update column statistic to localhash, not catalog */
+		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 04d12a7..42c82c8 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/progress.h"
@@ -72,6 +73,12 @@ static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
 							bool verbose, bool *pSwapToastByContent,
 							TransactionId *pFreezeXid, MultiXactId *pCutoffMulti);
 static List *get_tables_to_cluster(MemoryContext cluster_context);
+static void gtt_swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
+					bool swap_toast_by_content,
+					bool is_internal,
+					TransactionId frozenXid,
+					MultiXactId cutoffMulti,
+					Oid *mapped_tables);
 
 
 /*---------------------------------------------------------------------------
@@ -366,6 +373,14 @@ cluster_rel(Oid tableOid, Oid indexOid, int options)
 					 errmsg("cannot vacuum temporary tables of other sessions")));
 	}
 
+	if (RELATION_IS_GLOBAL_TEMP(OldHeap) &&
+		!gtt_storage_attached(RelationGetRelid(OldHeap)))
+	{
+		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.
@@ -750,6 +765,9 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
 	BlockNumber num_pages;
 	int			elevel = verbose ? INFO : DEBUG2;
 	PGRUsage	ru0;
+	bool		is_gtt = false;
+	uint32		gtt_relfrozenxid = 0;
+	uint32		gtt_relminmxid = 0;
 
 	pg_rusage_init(&ru0);
 
@@ -763,6 +781,9 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
 	else
 		OldIndex = NULL;
 
+	if (RELATION_IS_GLOBAL_TEMP(OldHeap))
+		is_gtt = true;
+
 	/*
 	 * Their tuple descriptors should be exactly alike, but here we only need
 	 * assume that they have the same number of columns.
@@ -830,20 +851,37 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
 						  &OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
 						  NULL);
 
-	/*
-	 * FreezeXid will become the table's new relfrozenxid, and that mustn't go
-	 * backwards, so take the max.
-	 */
-	if (TransactionIdIsValid(OldHeap->rd_rel->relfrozenxid) &&
-		TransactionIdPrecedes(FreezeXid, OldHeap->rd_rel->relfrozenxid))
-		FreezeXid = OldHeap->rd_rel->relfrozenxid;
+	if (is_gtt)
+	{
+		get_gtt_relstats(OIDOldHeap,
+					NULL, NULL, NULL,
+					&gtt_relfrozenxid, &gtt_relminmxid);
 
-	/*
-	 * MultiXactCutoff, similarly, shouldn't go backwards either.
-	 */
-	if (MultiXactIdIsValid(OldHeap->rd_rel->relminmxid) &&
-		MultiXactIdPrecedes(MultiXactCutoff, OldHeap->rd_rel->relminmxid))
-		MultiXactCutoff = OldHeap->rd_rel->relminmxid;
+		if (TransactionIdIsValid(gtt_relfrozenxid) &&
+			TransactionIdPrecedes(FreezeXid, gtt_relfrozenxid))
+			FreezeXid = gtt_relfrozenxid;
+
+		if (MultiXactIdIsValid(gtt_relminmxid) &&
+			MultiXactIdPrecedes(MultiXactCutoff, gtt_relminmxid))
+			MultiXactCutoff = gtt_relminmxid;
+	}
+	else
+	{
+		/*
+		 * FreezeXid will become the table's new relfrozenxid, and that mustn't go
+		 * backwards, so take the max.
+		 */
+		if (TransactionIdIsValid(OldHeap->rd_rel->relfrozenxid) &&
+			TransactionIdPrecedes(FreezeXid, OldHeap->rd_rel->relfrozenxid))
+			FreezeXid = OldHeap->rd_rel->relfrozenxid;
+
+		/*
+		 * MultiXactCutoff, similarly, shouldn't go backwards either.
+		 */
+		if (MultiXactIdIsValid(OldHeap->rd_rel->relminmxid) &&
+			MultiXactIdPrecedes(MultiXactCutoff, OldHeap->rd_rel->relminmxid))
+			MultiXactCutoff = OldHeap->rd_rel->relminmxid;
+	}
 
 	/*
 	 * Decide whether to use an indexscan or seqscan-and-optional-sort to scan
@@ -911,6 +949,12 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
 	table_close(OldHeap, NoLock);
 	table_close(NewHeap, NoLock);
 
+	if (is_gtt)
+	{
+		CommandCounterIncrement();
+		return;
+	}
+
 	/* Update pg_class to reflect the correct values of pages and tuples. */
 	relRelation = table_open(RelationRelationId, RowExclusiveLock);
 
@@ -1346,10 +1390,21 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
 	 * Swap the contents of the heap relations (including any toast tables).
 	 * Also set old heap's relfrozenxid to frozenXid.
 	 */
-	swap_relation_files(OIDOldHeap, OIDNewHeap,
+	if (newrelpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		Assert(!is_system_catalog);
+		gtt_swap_relation_files(OIDOldHeap, OIDNewHeap,
+								(OIDOldHeap == RelationRelationId),
+								swap_toast_by_content, is_internal,
+								frozenXid, cutoffMulti, mapped_tables);
+	}
+	else
+	{
+		swap_relation_files(OIDOldHeap, OIDNewHeap,
 						(OIDOldHeap == RelationRelationId),
 						swap_toast_by_content, is_internal,
 						frozenXid, cutoffMulti, mapped_tables);
+	}
 
 	/*
 	 * If it's a system catalog, queue a sinval message to flush all catcaches
@@ -1557,3 +1612,141 @@ get_tables_to_cluster(MemoryContext cluster_context)
 
 	return rvs;
 }
+
+static void
+gtt_swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
+					bool swap_toast_by_content,
+					bool is_internal,
+					TransactionId frozenXid,
+					MultiXactId cutoffMulti,
+					Oid *mapped_tables)
+{
+	Relation	relRelation;
+	Oid			relfilenode1,
+				relfilenode2;
+	Relation	rel1;
+	Relation	rel2;
+
+	relRelation = table_open(RelationRelationId, RowExclusiveLock);
+
+	rel1 = relation_open(r1, AccessExclusiveLock);
+	rel2 = relation_open(r2, AccessExclusiveLock);
+
+	relfilenode1 = gtt_fetch_current_relfilenode(r1);
+	relfilenode2 = gtt_fetch_current_relfilenode(r2);
+
+	Assert(OidIsValid(relfilenode1) && OidIsValid(relfilenode2));
+	gtt_switch_rel_relfilenode(r1, relfilenode1, r2, relfilenode2, true);
+
+	CacheInvalidateRelcache(rel1);
+	CacheInvalidateRelcache(rel2);
+
+	InvokeObjectPostAlterHookArg(RelationRelationId, r1, 0,
+								 InvalidOid, is_internal);
+	InvokeObjectPostAlterHookArg(RelationRelationId, r2, 0,
+								 InvalidOid, true);
+
+	if (rel1->rd_rel->reltoastrelid || rel2->rd_rel->reltoastrelid)
+	{
+		if (swap_toast_by_content)
+		{
+			if (rel1->rd_rel->reltoastrelid && rel2->rd_rel->reltoastrelid)
+			{
+				gtt_swap_relation_files(rel1->rd_rel->reltoastrelid,
+									rel2->rd_rel->reltoastrelid,
+									target_is_pg_class,
+									swap_toast_by_content,
+									is_internal,
+									frozenXid,
+									cutoffMulti,
+									mapped_tables);
+			}
+			else
+				elog(ERROR, "cannot swap toast files by content when there's only one");
+		}
+		else
+		{
+			ObjectAddress baseobject,
+						toastobject;
+			long		count;
+
+			if (IsSystemRelation(rel1))
+				elog(ERROR, "cannot swap toast files by links for system catalogs");
+
+			if (rel1->rd_rel->reltoastrelid)
+			{
+				count = deleteDependencyRecordsFor(RelationRelationId,
+												   rel1->rd_rel->reltoastrelid,
+												   false);
+				if (count != 1)
+					elog(ERROR, "expected one dependency record for TOAST table, found %ld",
+						 count);
+			}
+			if (rel2->rd_rel->reltoastrelid)
+			{
+				count = deleteDependencyRecordsFor(RelationRelationId,
+												   rel2->rd_rel->reltoastrelid,
+												   false);
+				if (count != 1)
+					elog(ERROR, "expected one dependency record for TOAST table, found %ld",
+						 count);
+			}
+
+			/* Register new dependencies */
+			baseobject.classId = RelationRelationId;
+			baseobject.objectSubId = 0;
+			toastobject.classId = RelationRelationId;
+			toastobject.objectSubId = 0;
+
+			if (rel1->rd_rel->reltoastrelid)
+			{
+				baseobject.objectId = r1;
+				toastobject.objectId = rel1->rd_rel->reltoastrelid;
+				recordDependencyOn(&toastobject, &baseobject,
+								   DEPENDENCY_INTERNAL);
+			}
+
+			if (rel2->rd_rel->reltoastrelid)
+			{
+				baseobject.objectId = r2;
+				toastobject.objectId = rel2->rd_rel->reltoastrelid;
+				recordDependencyOn(&toastobject, &baseobject,
+								   DEPENDENCY_INTERNAL);
+			}
+		}
+	}
+
+	if (swap_toast_by_content &&
+		rel1->rd_rel->relkind == RELKIND_TOASTVALUE &&
+		rel2->rd_rel->relkind == RELKIND_TOASTVALUE)
+	{
+		Oid			toastIndex1,
+					toastIndex2;
+
+		/* Get valid index for each relation */
+		toastIndex1 = toast_get_valid_index(r1,
+											AccessExclusiveLock);
+		toastIndex2 = toast_get_valid_index(r2,
+											AccessExclusiveLock);
+
+		gtt_swap_relation_files(toastIndex1,
+							toastIndex2,
+							target_is_pg_class,
+							swap_toast_by_content,
+							is_internal,
+							InvalidTransactionId,
+							InvalidMultiXactId,
+							mapped_tables);
+	}
+
+	relation_close(rel1, NoLock);
+	relation_close(rel2, NoLock);
+
+	table_close(relRelation, RowExclusiveLock);
+
+	RelationCloseSmgrByOid(r1);
+	RelationCloseSmgrByOid(r2);
+
+	CommandCounterIncrement();
+}
+
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index ac07f75..655184d 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -27,6 +27,7 @@
 #include "catalog/dependency.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
+#include "catalog/storage_gtt.h"
 #include "commands/copy.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -1063,7 +1064,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(rel))
 			PreventCommandIfReadOnly("COPY FROM");
 
 		cstate = BeginCopyFrom(pstate, rel, stmt->filename, stmt->is_program,
@@ -2794,6 +2795,8 @@ CopyFrom(CopyState cstate)
 
 	ExecOpenIndices(resultRelInfo, false);
 
+	init_gtt_storage(CMD_INSERT, resultRelInfo);
+
 	estate->es_result_relations = resultRelInfo;
 	estate->es_num_result_relations = 1;
 	estate->es_result_relation_info = resultRelInfo;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 2baca12..5338db9 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -521,6 +521,7 @@ DefineIndex(Oid relationId,
 	Snapshot	snapshot;
 	int			save_nestlevel = -1;
 	int			i;
+	char		rel_persistence;
 
 	/*
 	 * Some callers need us to run with an empty default_tablespace; this is a
@@ -542,7 +543,9 @@ 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)
+	rel_persistence = get_rel_persistence(relationId);
+	if (stmt->concurrent &&
+		!(rel_persistence == RELPERSISTENCE_TEMP || rel_persistence == RELPERSISTENCE_GLOBAL_TEMP))
 		concurrent = true;
 	else
 		concurrent = false;
@@ -2460,7 +2463,8 @@ ReindexIndex(RangeVar *indexRelation, int options, bool concurrent)
 	persistence = irel->rd_rel->relpersistence;
 	index_close(irel, NoLock);
 
-	if (concurrent && persistence != RELPERSISTENCE_TEMP)
+	if (concurrent &&
+		!(persistence == RELPERSISTENCE_TEMP || persistence == RELPERSISTENCE_GLOBAL_TEMP))
 		ReindexRelationConcurrently(indOid, options);
 	else
 		reindex_index(indOid, false, persistence,
@@ -2546,6 +2550,7 @@ ReindexTable(RangeVar *relation, int options, bool concurrent)
 {
 	Oid			heapOid;
 	bool		result;
+	char		rel_persistence;
 
 	/*
 	 * The lock level used here should match reindex_relation().
@@ -2560,7 +2565,9 @@ ReindexTable(RangeVar *relation, int options, bool concurrent)
 									   0,
 									   RangeVarCallbackOwnsTable, NULL);
 
-	if (concurrent && get_rel_persistence(heapOid) != RELPERSISTENCE_TEMP)
+	rel_persistence = get_rel_persistence(heapOid);
+	if (concurrent &&
+		!(rel_persistence == RELPERSISTENCE_TEMP || rel_persistence == RELPERSISTENCE_GLOBAL_TEMP))
 	{
 		result = ReindexRelationConcurrently(heapOid, options);
 
@@ -2761,12 +2768,15 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
 	foreach(l, relids)
 	{
 		Oid			relid = lfirst_oid(l);
+		char		rel_persistence;
 
 		StartTransactionCommand();
 		/* functions in indexes may want a snapshot set */
 		PushActiveSnapshot(GetTransactionSnapshot());
 
-		if (concurrent && get_rel_persistence(relid) != RELPERSISTENCE_TEMP)
+		rel_persistence = get_rel_persistence(relid);
+		if (concurrent &&
+			!(rel_persistence == RELPERSISTENCE_TEMP || rel_persistence == RELPERSISTENCE_GLOBAL_TEMP))
 		{
 			(void) ReindexRelationConcurrently(relid, options);
 			/* ReindexRelationConcurrently() does the verbose output */
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 6aab73b..a45863a 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -30,6 +30,8 @@
 #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"
@@ -94,7 +96,7 @@ static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
  */
 static SeqTableData *last_used_seq = NULL;
 
-static void fill_seq_with_data(Relation rel, HeapTuple tuple);
+static void fill_seq_with_data(Relation rel, HeapTuple tuple, Buffer buf);
 static Relation lock_and_open_sequence(SeqTable seq);
 static void create_seq_hashtable(void);
 static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
@@ -108,6 +110,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);
 
 
 /*
@@ -222,7 +225,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
 
 	/* now initialize the sequence's data */
 	tuple = heap_form_tuple(tupDesc, value, null);
-	fill_seq_with_data(rel, tuple);
+	fill_seq_with_data(rel, tuple, InvalidBuffer);
 
 	/* process OWNED BY if given */
 	if (owned_by)
@@ -327,7 +330,7 @@ ResetSequence(Oid seq_relid)
 	/*
 	 * Insert the modified tuple into the new storage file.
 	 */
-	fill_seq_with_data(seq_rel, tuple);
+	fill_seq_with_data(seq_rel, tuple, InvalidBuffer);
 
 	/* Clear local cache so that we don't think we have cached numbers */
 	/* Note that we do not change the currval() state */
@@ -340,17 +343,21 @@ ResetSequence(Oid seq_relid)
  * Initialize a sequence's relation with the specified tuple as content
  */
 static void
-fill_seq_with_data(Relation rel, HeapTuple tuple)
+fill_seq_with_data(Relation rel, HeapTuple tuple, Buffer buf)
 {
-	Buffer		buf;
 	Page		page;
 	sequence_magic *sm;
 	OffsetNumber offnum;
+	bool lockBuffer = false;
 
 	/* Initialize first page of relation with special magic number */
 
-	buf = ReadBuffer(rel, P_NEW);
-	Assert(BufferGetBlockNumber(buf) == 0);
+	if (buf == InvalidBuffer)
+	{
+		buf = ReadBuffer(rel, P_NEW);
+		Assert(BufferGetBlockNumber(buf) == 0);
+		lockBuffer = true;
+	}
 
 	page = BufferGetPage(buf);
 
@@ -360,7 +367,8 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 
 	/* Now insert sequence tuple */
 
-	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+	if (lockBuffer)
+		LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
 	/*
 	 * Since VACUUM does not process sequences, we have to force the tuple to
@@ -410,7 +418,8 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
 
 	END_CRIT_SECTION();
 
-	UnlockReleaseBuffer(buf);
+	if (lockBuffer)
+		UnlockReleaseBuffer(buf);
 }
 
 /*
@@ -451,6 +460,13 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
 
 	init_sequence(relid, &elm, &seqrel);
 
+	if (RELATION_IS_GLOBAL_TEMP(seqrel))
+	{
+		if (is_other_backend_use_gtt(RelationGetRelid(seqrel)))
+			elog(ERROR, "cannot alter global temp sequence %s when other backend attached it",
+						RelationGetRelationName(seqrel));
+	}
+
 	rel = table_open(SequenceRelationId, RowExclusiveLock);
 	seqtuple = SearchSysCacheCopy1(SEQRELID,
 								   ObjectIdGetDatum(relid));
@@ -502,7 +518,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
 		/*
 		 * Insert the modified tuple into the new storage file.
 		 */
-		fill_seq_with_data(seqrel, newdatatuple);
+		fill_seq_with_data(seqrel, newdatatuple, InvalidBuffer);
 	}
 
 	/* process OWNED BY if given */
@@ -611,7 +627,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(seqrel))
 		PreventCommandIfReadOnly("nextval()");
 
 	/*
@@ -936,7 +952,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(seqrel))
 		PreventCommandIfReadOnly("setval()");
 
 	/*
@@ -1154,6 +1170,13 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	/* Return results */
 	*p_elm = elm;
 	*p_rel = seqrel;
+
+	if (RELATION_IS_GLOBAL_TEMP(seqrel) &&
+		!gtt_storage_attached(RelationGetRelid(seqrel)))
+	{
+		RelationCreateStorage(seqrel->rd_node, RELPERSISTENCE_GLOBAL_TEMP, seqrel);
+		gtt_init_seq(seqrel);
+	}
 }
 
 
@@ -1954,3 +1977,46 @@ seq_mask(char *page, BlockNumber blkno)
 
 	mask_unused_space(page);
 }
+
+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;
+}
+
+void
+gtt_init_seq(Relation rel)
+{
+	/* Initialize sequence for global temporary tables */
+	Datum		value[SEQ_COL_LASTCOL] = {0};
+	bool		null[SEQ_COL_LASTCOL] = {false};
+	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 */
+
+	tuple = heap_form_tuple(RelationGetDescr(rel), value, null);
+	fill_seq_with_data(rel, tuple, InvalidBuffer);
+	heap_freetuple(tuple);
+
+	return;
+}
+
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 037d457..6eadc05 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"
@@ -555,6 +556,7 @@ static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
 								  Relation partitionTbl);
 static List *GetParentedForeignKeyRefs(Relation partition);
 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
+static OnCommitAction gtt_oncommit_option(List *options);
 
 
 /* ----------------------------------------------------------------
@@ -600,6 +602,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
@@ -610,8 +613,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Check consistency of arguments
 	 */
+	/* global temp table same as local temp table */
 	if (stmt->oncommit != ONCOMMIT_NOOP
-		&& stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
+		&& !(stmt->relation->relpersistence == RELPERSISTENCE_TEMP ||
+			 stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 				 errmsg("ON COMMIT can only be used on temporary tables")));
@@ -641,7 +646,9 @@ 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
+	/* global temp table same as local temp table */
+	if ((stmt->relation->relpersistence == RELPERSISTENCE_TEMP ||
+		 stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 		&& InSecurityRestrictedOperation())
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -742,6 +749,55 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
+	/* global temp table */
+	oncommit_action = gtt_oncommit_option(stmt->options);
+	if (stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP &&
+		(relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE))
+	{
+		/* check parent table*/
+		if (inheritOids)
+		{
+			Oid			parent = linitial_oid(inheritOids);
+			Relation	relation = table_open(parent, NoLock);
+
+			if (!RELATION_IS_GLOBAL_TEMP(relation))
+				elog(ERROR, "The parent table must be global temporary table");
+
+			table_close(relation, NoLock);
+		}
+
+		if (oncommit_action != ONCOMMIT_NOOP)
+		{
+			if (stmt->oncommit != ONCOMMIT_NOOP)
+				elog(ERROR, "could not create global temporary table with on commit and with clause at same time");
+
+			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
+				elog(ERROR, "global temp table not support on commit drop clause");
+
+			stmt->options = lappend(stmt->options, opt);
+		}
+	}
+	else if (oncommit_action != ONCOMMIT_NOOP)
+		elog(ERROR, "The parameter on_commit_delete_rows is exclusive to the global temp table, which cannot be specified by a regular table");
+
 	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
 									 true, false);
 
@@ -1329,6 +1385,7 @@ RemoveRelations(DropStmt *drop)
 		Oid			relOid;
 		ObjectAddress obj;
 		struct DropRelationCallbackState state;
+		char		rel_persistence;
 
 		/*
 		 * These next few steps are a great deal like relation_openrv, but we
@@ -1362,8 +1419,9 @@ RemoveRelations(DropStmt *drop)
 		 * Decide if concurrent mode needs to be used here or not.  The
 		 * relation persistence cannot be known without its OID.
 		 */
+		rel_persistence = get_rel_persistence(relOid);
 		if (drop->concurrent &&
-			get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
+			!(rel_persistence == RELPERSISTENCE_TEMP || rel_persistence == RELPERSISTENCE_GLOBAL_TEMP))
 		{
 			Assert(list_length(drop->objects) == 1 &&
 				   drop->removeType == OBJECT_INDEX);
@@ -1818,6 +1876,10 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
 		if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 			continue;
 
+		if (RELATION_IS_GLOBAL_TEMP(rel) &&
+			!gtt_storage_attached(RelationGetRelid(rel)))
+			continue;
+
 		/*
 		 * Normally, we need a transaction-safe truncation here.  However, if
 		 * the table was either created in the current (sub)transaction or has
@@ -3569,6 +3631,14 @@ AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
 	/* Caller is required to provide an adequate lock. */
 	rel = relation_open(context->relid, NoLock);
 
+	/* We allow to alter global temp table only this session use it */
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+	{
+		if (is_other_backend_use_gtt(RelationGetRelid(rel)))
+			elog(ERROR, "can not alter table %s when other backend attached this global temp table",
+						RelationGetRelationName(rel));
+	}
+
 	CheckTableNotInUse(rel, "ALTER TABLE");
 
 	ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
@@ -4846,6 +4916,38 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("cannot rewrite temporary tables of other sessions")));
 
+			if (RELATION_IS_GLOBAL_TEMP(OldHeap))
+			{
+				if (tab->chgPersistence)
+					ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("cannot change global temporary table persistence setting")));
+
+				/* gtt may not attached, create it */
+				if(!gtt_storage_attached(tab->relid))
+				{
+					ResultRelInfo *resultRelInfo;
+					MemoryContext oldcontext;
+					MemoryContext ctx_alter_gtt;
+
+					ctx_alter_gtt = AllocSetContextCreate(CurrentMemoryContext,
+											"gtt alter table", ALLOCSET_DEFAULT_SIZES);
+
+					oldcontext = MemoryContextSwitchTo(ctx_alter_gtt);
+					resultRelInfo = makeNode(ResultRelInfo);
+					InitResultRelInfo(resultRelInfo, OldHeap,
+									1, NULL, 0);
+					if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
+						resultRelInfo->ri_IndexRelationDescs == NULL)
+						ExecOpenIndices(resultRelInfo, false);
+
+					init_gtt_storage(CMD_UTILITY, resultRelInfo);
+					ExecCloseIndices(resultRelInfo);
+					MemoryContextSwitchTo(oldcontext);
+					MemoryContextDelete(ctx_alter_gtt);
+				}
+			}
+
 			/*
 			 * Select destination tablespace (same as original unless user
 			 * requested a change)
@@ -8183,6 +8285,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;
 	}
 
 	/*
@@ -12739,6 +12847,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 	if (defList == NIL && operation != AT_ReplaceRelOptions)
 		return;					/* nothing to do */
 
+	if (gtt_oncommit_option(defList) != ONCOMMIT_NOOP)
+		elog(ERROR, "table cannot add or modify on commit parameter by ALTER TABLE command.");
+
 	pgclass = table_open(RelationRelationId, RowExclusiveLock);
 
 	/* Fetch heap tuple */
@@ -12941,6 +13052,9 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 	 */
 	rel = relation_open(tableOid, lockmode);
 
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+		elog(ERROR, "not support alter table set tablespace on global temp table");
+
 	/*
 	 * No work if no change in tablespace.
 	 */
@@ -13315,7 +13429,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(rel->rd_smgr, dstrel, MAIN_FORKNUM,
@@ -14722,7 +14836,9 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
 	 */
 	switch (rel->rd_rel->relpersistence)
 	{
+		/* global temp table same as local temp table */
 		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",
@@ -17315,3 +17431,36 @@ ATDetachCheckNoForeignKeyRefs(Relation partition)
 		table_close(rel, NoLock);
 	}
 }
+
+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);
+
+			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 5a110ed..02e6f1e 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -35,6 +35,7 @@
 #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/defrem.h"
 #include "commands/vacuum.h"
@@ -1215,6 +1216,17 @@ vac_update_relstats(Relation relation,
 	HeapTuple	ctup;
 	Form_pg_class pgcform;
 	bool		dirty;
+	bool		is_gtt = false;
+
+	 /* global temp table remember relstats to localhash and rel->rd_rel, not catalog */
+	if (RELATION_IS_GLOBAL_TEMP(relation))
+	{
+		is_gtt = true;
+		up_gtt_relstats(relation,
+						num_pages, num_tuples,
+						num_all_visible_pages,
+						frozenxid, minmulti);
+	}
 
 	rd = table_open(RelationRelationId, RowExclusiveLock);
 
@@ -1228,17 +1240,26 @@ vac_update_relstats(Relation relation,
 	/* Apply statistical updates, if any, to copied tuple */
 
 	dirty = false;
-	if (pgcform->relpages != (int32) num_pages)
+
+	if (is_gtt)
+		relation->rd_rel->relpages = (int32) num_pages;
+	else if (pgcform->relpages != (int32) num_pages)
 	{
 		pgcform->relpages = (int32) num_pages;
 		dirty = true;
 	}
-	if (pgcform->reltuples != (float4) num_tuples)
+
+	if (is_gtt)
+		relation->rd_rel->reltuples = (float4) num_tuples;
+	else if (pgcform->reltuples != (float4) num_tuples)
 	{
 		pgcform->reltuples = (float4) num_tuples;
 		dirty = true;
 	}
-	if (pgcform->relallvisible != (int32) num_all_visible_pages)
+
+	if (is_gtt)
+		relation->rd_rel->relallvisible = (int32) num_all_visible_pages;
+	else if (pgcform->relallvisible != (int32) num_all_visible_pages)
 	{
 		pgcform->relallvisible = (int32) num_all_visible_pages;
 		dirty = true;
@@ -1283,7 +1304,8 @@ vac_update_relstats(Relation relation,
 	 * This should match vac_update_datfrozenxid() concerning what we consider
 	 * to be "in the future".
 	 */
-	if (TransactionIdIsNormal(frozenxid) &&
+	if (!is_gtt &&
+		TransactionIdIsNormal(frozenxid) &&
 		pgcform->relfrozenxid != frozenxid &&
 		(TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid) ||
 		 TransactionIdPrecedes(ReadNewTransactionId(),
@@ -1294,7 +1316,8 @@ vac_update_relstats(Relation relation,
 	}
 
 	/* Similarly for relminmxid */
-	if (MultiXactIdIsValid(minmulti) &&
+	if (!is_gtt &&
+		MultiXactIdIsValid(minmulti) &&
 		pgcform->relminmxid != minmulti &&
 		(MultiXactIdPrecedes(pgcform->relminmxid, minmulti) ||
 		 MultiXactIdPrecedes(ReadNextMultiXactId(), pgcform->relminmxid)))
@@ -1392,6 +1415,10 @@ vac_update_datfrozenxid(void)
 			continue;
 		}
 
+		/* global temp table relstats not in pg_class */
+		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
@@ -1449,6 +1476,42 @@ vac_update_datfrozenxid(void)
 	Assert(TransactionIdIsNormal(newFrozenXid));
 	Assert(MultiXactIdIsValid(newMinMulti));
 
+	/*
+	 * Global temp table get frozenxid from MyProc
+	 * to avoid the vacuum truncate clog that gtt need.
+	 */
+	if (max_active_gtt > 0)
+	{
+		TransactionId	safe_age;
+		TransactionId	oldest_gtt_frozenxid =
+			list_all_session_gtt_frozenxids(0, NULL, 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 temp table oldest relfrozenxid %u is the oldest in the entire db",
+							oldest_gtt_frozenxid),
+					 errdetail("The oldest relfrozenxid in pg_class is %u", newFrozenXid),
+					 errhint("If they differ greatly, please consider cleaning up the data in global temp table.")));
+			}
+
+			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);
 
@@ -1776,6 +1839,15 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
 		return false;
 	}
 
+	if (RELATION_IS_GLOBAL_TEMP(onerel) &&
+		!gtt_storage_attached(RelationGetRelid(onerel)))
+	{
+		relation_close(onerel, 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 6e65103..04706ee 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -530,6 +530,11 @@ DefineView(ViewStmt *stmt, const char *queryString,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("views cannot be unlogged because they do not have storage")));
 
+	if (stmt->view->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("views cannot be global temp 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 4fdffad..0aa7559 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -787,6 +787,9 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 		if (isTempNamespace(get_rel_namespace(rte->relid)))
 			continue;
 
+		if (get_rel_persistence(rte->relid) == RELPERSISTENCE_GLOBAL_TEMP)
+			continue;
+
 		PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt));
 	}
 
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index fb6ce49..06d9237 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -18,6 +18,7 @@
 #include "catalog/partition.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_type.h"
+#include "catalog/storage_gtt.h"
 #include "executor/execPartition.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
@@ -545,6 +546,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
 						(node != NULL &&
 						 node->onConflictAction != ONCONFLICT_NONE));
 
+	init_gtt_storage(mtstate->operation, leaf_part_rri);
+
 	/*
 	 * Build WITH CHECK OPTION constraints for the partition.  Note that we
 	 * didn't build the withCheckOptionList for partitions within the planner,
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 20a4c47..e9f6dcd 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -42,6 +42,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"
@@ -2379,6 +2380,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			ExecOpenIndices(resultRelInfo,
 							node->onConflictAction != ONCONFLICT_NONE);
 
+		init_gtt_storage(operation, resultRelInfo);
+
 		/*
 		 * If this is an UPDATE and a BEFORE UPDATE trigger is present, the
 		 * trigger itself might modify the partition-key values. So arrange
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 255f56b..d94d6f0 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -591,6 +591,8 @@ static void
 set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
 						  RangeTblEntry *rte)
 {
+	char relpersistence;
+
 	/*
 	 * The flag has previously been initialized to false, so we can just
 	 * return if it becomes clear that we can't safely set it.
@@ -618,7 +620,11 @@ 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)
+			/* global temp table is same as local temp table */
+			relpersistence = get_rel_persistence(rte->relid);
+
+			if (relpersistence == RELPERSISTENCE_TEMP ||
+				relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 				return;
 
 			/*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index e664eb1..e03bfa0 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6419,7 +6419,9 @@ plan_create_index_workers(Oid tableOid, Oid indexOid)
 	 * Furthermore, any index predicate or index expressions must be parallel
 	 * safe.
 	 */
+	/* global temp table is same as local temp table */
 	if (heap->rd_rel->relpersistence == RELPERSISTENCE_TEMP ||
+		heap->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP ||
 		!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 2554502..04fb5d4 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -28,6 +28,7 @@
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
+#include "catalog/storage_gtt.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_statistic_ext.h"
@@ -221,6 +222,14 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 				continue;
 			}
 
+			/* Ignore empty index for global temp table */
+			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 eee9c33..1891c53 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -2594,6 +2594,11 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("materialized views must not use temporary tables or views")));
 
+		if (is_query_using_gtt(query))
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("materialized views must not use global temporary tables or views")));
+
 		/*
 		 * A materialized view would either need to save parameters for use in
 		 * maintaining/loading the data or prohibit them entirely.  The latter
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3c78f2d..edb8d9f 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3302,17 +3302,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; }
@@ -11648,19 +11642,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 b875a50..123945b 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -81,6 +81,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 is_query_using_gtt_walker(Node *node, void *context);
 
 
 /*
@@ -3579,3 +3580,49 @@ isQueryUsingTempRelation_walker(Node *node, void *context)
 								  isQueryUsingTempRelation_walker,
 								  context);
 }
+
+/* check if the query uses global temp table */
+static bool
+is_query_using_gtt_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 = relation_open(rte->relid, AccessShareLock);
+				char		relpersistence = rel->rd_rel->relpersistence;
+
+				relation_close(rel, AccessShareLock);
+				if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+					return true;
+			}
+		}
+
+		return query_tree_walker(query,
+								 is_query_using_gtt_walker,
+								 context,
+								 QTW_IGNORE_JOINALIASES);
+	}
+
+	return expression_tree_walker(node,
+								  is_query_using_gtt_walker,
+								  context);
+}
+
+/* check if the query uses global temp table */
+bool
+is_query_using_gtt(Query *query)
+{
+	return is_query_using_gtt_walker((Node *) query, NULL);
+}
+
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 75c122f..97213ed 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -437,6 +437,9 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
 	seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
 	seqstmt->options = seqoptions;
 
+	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
@@ -3108,6 +3111,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 		cxt.isforeign = false;
 	}
 	cxt.relation = stmt->relation;
+	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 7e97ffa..1f2ac58 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2089,6 +2089,11 @@ do_autovacuum(void)
 			}
 			continue;
 		}
+		else if (classForm->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		{
+			/* autovacuum skip vacuum global temp table */
+			continue;
+		}
 
 		/* Fetch reloptions and the pgstat entry for this table */
 		relopts = extract_autovac_opts(tuple, pg_class_desc);
@@ -2155,7 +2160,9 @@ do_autovacuum(void)
 		/*
 		 * We cannot safely process other backends' temp tables, so skip 'em.
 		 */
-		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+		/* autovacuum skip vacuum global temp table */
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP || 
+			classForm->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 			continue;
 
 		relid = classForm->oid;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index f9980cf..406a25c 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -37,6 +37,7 @@
 #include "access/xlog.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"
@@ -53,6 +54,7 @@
 #include "utils/rel.h"
 #include "utils/resowner_private.h"
 #include "utils/timestamp.h"
+#include "utils/guc.h"
 
 
 /* Note: these two macros only work on shared buffers, not local ones! */
@@ -2819,6 +2821,16 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln)
 BlockNumber
 RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 {
+	/*
+	 * When this backend not init gtt storage
+	 * return 0
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(relation) &&
+		!gtt_storage_attached(RelationGetRelid(relation)))
+	{
+		return 0;
+	}
+
 	switch (relation->rd_rel->relkind)
 	{
 		case RELKIND_SEQUENCE:
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 427b0d5..7adb969 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -21,6 +21,7 @@
 #include "access/nbtree.h"
 #include "access/subtrans.h"
 #include "access/twophase.h"
+#include "catalog/storage_gtt.h"
 #include "commands/async.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -147,6 +148,7 @@ CreateSharedMemoryAndSemaphores(void)
 		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
@@ -217,6 +219,8 @@ CreateSharedMemoryAndSemaphores(void)
 	SUBTRANSShmemInit();
 	MultiXactShmemInit();
 	InitBufferPool();
+	/* global temporary table */
+	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 3630006..0c8ef80 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -63,6 +63,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))))
 
@@ -4086,3 +4087,77 @@ KnownAssignedXidsReset(void)
 
 	LWLockRelease(ProcArrayLock);
 }
+
+/*
+ * search all active backend to get oldest frozenxid
+ * for global temp table.
+ */
+int
+list_all_session_gtt_frozenxids(int max_size, int *pids, uint32 *xids, int *n)
+{
+	ProcArrayStruct *arrayP = procArray;
+	TransactionId result = InvalidTransactionId;
+	int			index;
+	int			flags = 0;
+	int			i = 0;
+
+	if (max_active_gtt <= 0)
+		return 0;
+
+	if (max_size > 0)
+	{
+		Assert(pids);
+		Assert(xids);
+		Assert(n);
+		*n = 0;
+	}
+
+	if (max_active_gtt <= 0)
+		return InvalidTransactionId;
+
+	if (RecoveryInProgress())
+		return InvalidTransactionId;
+
+	flags |= PROC_IS_AUTOVACUUM;
+	flags |= PROC_IN_LOGICAL_DECODING;
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+	if (max_size > 0 && max_size < arrayP->numProcs)
+	{
+		LWLockRelease(ProcArrayLock);
+		elog(ERROR, "list_all_gtt_frozenxids require more array");
+	}
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		int			pgprocno = arrayP->pgprocnos[index];
+		volatile PGPROC *proc = &allProcs[pgprocno];
+		volatile PGXACT *pgxact = &allPgXact[pgprocno];
+
+		if (pgxact->vacuumFlags & flags)
+			continue;
+
+		if (proc->databaseId == MyDatabaseId &&
+			TransactionIdIsNormal(proc->session_gtt_frozenxid))
+		{
+			if (result == InvalidTransactionId)
+				result = proc->session_gtt_frozenxid;
+			else if (TransactionIdPrecedes(proc->session_gtt_frozenxid, result))
+				result = proc->session_gtt_frozenxid;
+
+			if (max_size > 0)
+			{
+				pids[i] = proc->pid;
+				xids[i] = proc->session_gtt_frozenxid;
+				i++;
+			}
+		}
+	}
+	LWLockRelease(ProcArrayLock);
+
+	if (max_size > 0)
+		*n = i;
+
+	return result;
+}
+
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 5aa19d3..b3bc455 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -395,6 +395,7 @@ InitProcess(void)
 	MyProc->databaseId = InvalidOid;
 	MyProc->roleId = InvalidOid;
 	MyProc->tempNamespaceId = InvalidOid;
+	MyProc->session_gtt_frozenxid = InvalidTransactionId;	/* init session level gtt frozenxid */
 	MyProc->isBackgroundWorker = IsBackgroundWorker;
 	MyProc->delayChkpt = false;
 	MyPgXact->vacuumFlags = 0;
@@ -577,6 +578,7 @@ InitAuxiliaryProcess(void)
 	MyProc->databaseId = InvalidOid;
 	MyProc->roleId = InvalidOid;
 	MyProc->tempNamespaceId = InvalidOid;
+	MyProc->session_gtt_frozenxid = InvalidTransactionId;	/* init session level gtt frozenxid */
 	MyProc->isBackgroundWorker = IsBackgroundWorker;
 	MyProc->delayChkpt = false;
 	MyPgXact->vacuumFlags = 0;
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 8406644..0d99321 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -1008,6 +1008,9 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 				Assert(backend != InvalidBackendId);
 			}
 			break;
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			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 4fdcb07..58fc8de 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -112,6 +112,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"
@@ -4731,12 +4732,25 @@ 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)
+							{
+								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))
 							{
@@ -4861,15 +4875,27 @@ 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)
+		{
+			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))
 		{
@@ -6251,6 +6277,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;
@@ -6268,6 +6295,13 @@ 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)
+		{
+			vardata.statsTuple = get_gtt_att_statistic(relid,
+													colnum,
+													rte->inh);
+			vardata.freefunc = release_gtt_statistic_cache;
+		}
 		else
 		{
 			vardata.statsTuple = SearchSysCache3(STATRELATTINH,
@@ -6279,6 +6313,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;
@@ -6294,6 +6330,13 @@ 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)
+		{
+			vardata.statsTuple = get_gtt_att_statistic(relid,
+													colnum,
+													false);
+			vardata.freefunc = release_gtt_statistic_cache;
+		}
 		else
 		{
 			vardata.statsTuple = SearchSysCache3(STATRELATTINH,
@@ -7212,6 +7255,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))
@@ -7224,6 +7269,14 @@ 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)
+			{
+				vardata.statsTuple =
+					get_gtt_att_statistic(rte->relid,
+										attnum,
+										false);
+				vardata.freefunc = release_gtt_statistic_cache;
+			}
 			else
 			{
 				vardata.statsTuple =
@@ -7236,6 +7289,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.
@@ -7255,6 +7310,14 @@ 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)
+			{
+				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 a7d63f1..d8a52cb 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"
@@ -2939,6 +2940,18 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
 		if (stawidth > 0)
 			return stawidth;
 	}
+	if (get_rel_persistence(relid) == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		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 9f1f11d..aeb2a78 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"
@@ -1133,6 +1134,24 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 				relation->rd_islocaltemp = false;
 			}
 			break;
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			{
+				BlockNumber     relpages = 0;
+				double          reltuples = 0;
+				BlockNumber     relallvisible = 0;
+
+				relation->rd_backend = BackendIdForTempRelations();
+				relation->rd_islocaltemp = false;
+				get_gtt_relstats(RelationGetRelid(relation),
+								&relpages,
+								&reltuples,
+								&relallvisible,
+								NULL, NULL);
+				relation->rd_rel->relpages = (int32)relpages;
+				relation->rd_rel->reltuples = (float4)reltuples;
+				relation->rd_rel->relallvisible = (int32)relallvisible;
+			}
+			break;
 		default:
 			elog(ERROR, "invalid relpersistence: %c",
 				 relation->rd_rel->relpersistence);
@@ -1187,6 +1206,7 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 		case RELKIND_PARTITIONED_INDEX:
 			Assert(relation->rd_rel->relam != InvalidOid);
 			RelationInitIndexAccessInfo(relation);
+			gtt_fix_index_state(relation);
 			break;
 		case RELKIND_RELATION:
 		case RELKIND_TOASTVALUE:
@@ -1317,7 +1337,17 @@ RelationInitPhysicalAddr(Relation relation)
 			heap_freetuple(phys_tuple);
 		}
 
-		relation->rd_node.relNode = relation->rd_rel->relfilenode;
+		if (RELATION_IS_GLOBAL_TEMP(relation))
+		{
+			Oid newrelnode = gtt_fetch_current_relfilenode(RelationGetRelid(relation));
+			if (newrelnode != InvalidOid &&
+				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
 	{
@@ -2246,6 +2276,8 @@ RelationReloadIndexInfo(Relation relation)
 							   HeapTupleHeaderGetXmin(tuple->t_data));
 
 		ReleaseSysCache(tuple);
+
+		gtt_fix_index_state(relation);
 	}
 
 	/* Okay, now it's valid again */
@@ -3472,6 +3504,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;
@@ -3579,28 +3615,34 @@ void
 RelationSetNewRelfilenode(Relation relation, char persistence)
 {
 	Oid			newrelfilenode;
-	Relation	pg_class;
-	HeapTuple	tuple;
+	Relation	pg_class = NULL;
+	HeapTuple	tuple = NULL;
 	Form_pg_class classform;
 	MultiXactId minmulti = InvalidMultiXactId;
 	TransactionId freezeXid = InvalidTransactionId;
 	RelFileNode newrnode;
+	bool		modify_pg_class = !RELATION_IS_GLOBAL_TEMP(relation);
 
 	/* Allocate a new relfilenode */
 	newrelfilenode = GetNewRelFileNode(relation->rd_rel->reltablespace, NULL,
 									   persistence);
 
-	/*
-	 * Get a writable copy of the pg_class tuple for the given relation.
-	 */
-	pg_class = table_open(RelationRelationId, RowExclusiveLock);
+	if (modify_pg_class)
+	{
+		/*
+		 * Get a writable copy of the pg_class tuple for the given relation.
+		 */
+		pg_class = table_open(RelationRelationId, RowExclusiveLock);
 
-	tuple = SearchSysCacheCopy1(RELOID,
-								ObjectIdGetDatum(RelationGetRelid(relation)));
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "could not find tuple for relation %u",
-			 RelationGetRelid(relation));
-	classform = (Form_pg_class) GETSTRUCT(tuple);
+		tuple = SearchSysCacheCopy1(RELOID,
+									ObjectIdGetDatum(RelationGetRelid(relation)));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "could not find tuple for relation %u",
+				 RelationGetRelid(relation));
+		classform = (Form_pg_class) GETSTRUCT(tuple);
+	}
+	else
+		memset(&classform, 0, sizeof(classform));
 
 	/*
 	 * Schedule unlinking of the old storage at transaction commit.
@@ -3626,7 +3668,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);
 			}
 			break;
@@ -3646,6 +3688,15 @@ RelationSetNewRelfilenode(Relation relation, char persistence)
 			break;
 	}
 
+	if (!modify_pg_class)
+	{
+		Oid relnode = gtt_fetch_current_relfilenode(RelationGetRelid(relation));
+
+		Assert(RELATION_IS_GLOBAL_TEMP(relation));
+		Assert(!RelationIsMapped(relation));
+		relation->rd_node.relNode = relnode;
+		CacheInvalidateRelcache(relation);
+	}
 	/*
 	 * If we're dealing with a mapped index, pg_class.relfilenode doesn't
 	 * change; instead we have to send the update to the relation mapper.
@@ -3655,7 +3706,7 @@ RelationSetNewRelfilenode(Relation relation, char persistence)
 	 * possibly-inaccurate values of relpages etc, but those will be fixed up
 	 * later.
 	 */
-	if (RelationIsMapped(relation))
+	else if (RelationIsMapped(relation))
 	{
 		/* This case is only supported for indexes */
 		Assert(relation->rd_rel->relkind == RELKIND_INDEX);
@@ -3701,9 +3752,12 @@ RelationSetNewRelfilenode(Relation relation, char persistence)
 		CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
 	}
 
-	heap_freetuple(tuple);
+	if (modify_pg_class)
+	{
+		heap_freetuple(tuple);
 
-	table_close(pg_class, RowExclusiveLock);
+		table_close(pg_class, RowExclusiveLock);
+	}
 
 	/*
 	 * Make the pg_class row change or relation map change visible.  This will
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5bdc02f..b52b51d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -37,6 +37,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/trigger.h"
@@ -142,6 +143,18 @@ char	   *GUC_check_errmsg_string;
 char	   *GUC_check_errdetail_string;
 char	   *GUC_check_errhint_string;
 
+/*
+ * num = 0 means disable global temp table feature.
+ * global temp table define can still storage in catalog
+ * just can not use.
+ * num > 0 means database can management num active global temp table.
+ */
+#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,
@@ -2070,6 +2083,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("Forces a switch to the next WAL file if a "
 						 "new file has not been started within N seconds."),
@@ -2577,6 +2599,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 5db4f57..018a9bf 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -2424,6 +2424,10 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
 		dopt->no_unlogged_table_data)
 		return;
 
+	/* Don't dump data in global temp 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))
@@ -15777,6 +15781,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
 		char	   *ftoptions = NULL;
 		char	   *srvname = NULL;
 		char	   *foreign = "";
+		char	   *table_type = NULL;
 
 		switch (tbinfo->relkind)
 		{
@@ -15830,9 +15835,15 @@ dumpTableSchema(Archive *fout, 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);
 
@@ -16226,7 +16237,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
 		 */
 		if (dopt->binary_upgrade &&
 			(tbinfo->relkind == RELKIND_RELATION ||
-			 tbinfo->relkind == RELKIND_MATVIEW))
+			 tbinfo->relkind == RELKIND_MATVIEW) &&
+			 tbinfo->relpersistence != RELPERSISTENCE_GLOBAL_TEMP)
 		{
 			appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
 			appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
@@ -17131,6 +17143,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	PQExpBuffer query = createPQExpBuffer();
 	PQExpBuffer delqry = createPQExpBuffer();
 	char	   *qseqname;
+	bool	   global_temp_seq = false;
 
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
@@ -17140,9 +17153,11 @@ dumpSequence(Archive *fout, 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 if (fout->remoteVersion >= 80400)
@@ -17187,6 +17202,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
 	cache = PQgetvalue(res, 0, 5);
 	cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
 
+	if (fout->remoteVersion >= 130000)
+		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)
@@ -17261,9 +17279,13 @@ dumpSequence(Archive *fout, 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 00aef85..9ad71c8 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -84,7 +84,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();
 
@@ -157,7 +157,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 7e524ea..371168f 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_gtt);
 static void free_rel_infos(RelInfoArr *rel_arr);
 static void print_db_infos(DbInfoArr *dbinfo);
 static void print_rel_infos(RelInfoArr *rel_arr);
@@ -304,9 +304,11 @@ print_maps(FileNameMap *maps, int n_maps, const char *db_name)
  *
  * 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_gtt)
 {
 	int			dbnum;
 
@@ -316,7 +318,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_gtt);
 
 	if (cluster == &old_cluster)
 		pg_log(PG_VERBOSE, "\nsource databases:\n");
@@ -404,7 +406,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_gtt)
 {
 	PGconn	   *conn = connectToServer(cluster,
 									   dbinfo->db_name);
@@ -441,22 +443,46 @@ get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
 	 * output, so we have to copy that system table.  It's easiest to do that
 	 * by treating it as a user table.
 	 */
-	snprintf(query + strlen(query), sizeof(query) - strlen(query),
-			 "WITH regular_heap (reloid, indtable, toastheap) AS ( "
-			 "  SELECT c.oid, 0::oid, 0::oid "
-			 "  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 "
-	/* exclude possible orphaned temp tables */
-			 "    ((n.nspname !~ '^pg_temp_' AND "
-			 "      n.nspname !~ '^pg_toast_temp_' AND "
-			 "      n.nspname NOT IN ('pg_catalog', 'information_schema', "
-			 "                        'binary_upgrade', 'pg_toast') AND "
-			 "      c.oid >= %u::pg_catalog.oid) OR "
-			 "     (n.nspname = 'pg_catalog' AND "
-			 "      relname IN ('pg_largeobject') ))), ",
-			 FirstNormalObjectId);
+	if (skip_gtt)
+	{
+		snprintf(query + strlen(query), sizeof(query) - strlen(query),
+				 "WITH regular_heap (reloid, indtable, toastheap) AS ( "
+				 "  SELECT c.oid, 0::oid, 0::oid "
+				 "  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 "
+		/* exclude global temp tables */
+				 "    relpersistence != " CppAsString2(RELPERSISTENCE_GLOBAL_TEMP) " AND "
+		/* exclude possible orphaned temp tables */
+				 "    ((n.nspname !~ '^pg_temp_' AND "
+				 "      n.nspname !~ '^pg_toast_temp_' AND "
+				 "      n.nspname NOT IN ('pg_catalog', 'information_schema', "
+				 "                        'binary_upgrade', 'pg_toast') AND "
+				 "      c.oid >= %u::pg_catalog.oid) OR "
+				 "     (n.nspname = 'pg_catalog' AND "
+				 "      relname IN ('pg_largeobject') ))), ",
+				 FirstNormalObjectId);
+	}
+	else
+	{
+		snprintf(query + strlen(query), sizeof(query) - strlen(query),
+				 "WITH regular_heap (reloid, indtable, toastheap) AS ( "
+				 "  SELECT c.oid, 0::oid, 0::oid "
+				 "  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 "
+		/* exclude possible orphaned temp tables */
+				 "    ((n.nspname !~ '^pg_temp_' AND "
+				 "      n.nspname !~ '^pg_toast_temp_' AND "
+				 "      n.nspname NOT IN ('pg_catalog', 'information_schema', "
+				 "                        'binary_upgrade', 'pg_toast') AND "
+				 "      c.oid >= %u::pg_catalog.oid) OR "
+				 "     (n.nspname = 'pg_catalog' AND "
+				 "      relname IN ('pg_largeobject') ))), ",
+				 FirstNormalObjectId);
+	}
 
 	/*
 	 * Add a CTE that collects OIDs of toast tables belonging to the tables
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 70194eb..8dd9e66 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);
 }
 
 /*
@@ -642,7 +642,9 @@ 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 "
+									  " relpersistence != " CppAsString2(RELPERSISTENCE_GLOBAL_TEMP) " AND "
+									  "relkind IN ("
 									  CppAsString2(RELKIND_RELATION) ", "
 									  CppAsString2(RELKIND_MATVIEW) ", "
 									  CppAsString2(RELKIND_TOASTVALUE) ")",
@@ -653,7 +655,9 @@ 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 "
+								  " 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 8b90cef..d155205 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -389,7 +389,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_gtt);
 void		print_maps(FileNameMap *maps, int n,
 					   const char *db_name);
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index f05e914..19adab9 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3724,7 +3724,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 		if (pset.sversion >= 90100)
 		{
 			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 f6fd623..ea87bc4 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1023,6 +1023,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},
@@ -2394,6 +2396,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 INDEX --- is allowed inside CREATE SCHEMA, so use TailMatches */
 	/* First off we complete CREATE UNIQUE with "INDEX" */
@@ -2602,6 +2607,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");
 	/* 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 cbfdfe2..be39472 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -59,7 +59,8 @@ extern Relation heap_create(const char *relname,
 							bool mapped_relation,
 							bool allow_system_table_mods,
 							TransactionId *relfrozenxid,
-							MultiXactId *relminmxid);
+							MultiXactId *relminmxid,
+							bool skip_create_storage);
 
 extern Oid	heap_create_with_catalog(const char *relname,
 									 Oid relnamespace,
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 78b33b2..4be89f6 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -168,6 +168,7 @@ typedef FormData_pg_class *Form_pg_class;
 #define		  RELPERSISTENCE_PERMANENT	'p' /* regular table */
 #define		  RELPERSISTENCE_UNLOGGED	'u' /* unlogged permanent table */
 #define		  RELPERSISTENCE_TEMP		't' /* temporary table */
+#define		  RELPERSISTENCE_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 4bce3ad..c8236a1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5558,6 +5558,40 @@
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_xact_function_self_time' },
 
+# For global temporary table
+{ oid => '4388',
+  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 => '4389',
+  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 => '4390',
+  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 => '4391',
+  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 30c38e0..7ff2408 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 0000000..902375d
--- /dev/null
+++ b/src/include/catalog/storage_gtt.h
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 Bitmapset *copy_active_gtt_bitmap(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 up_gtt_relstats(Relation relation,
+							BlockNumber num_pages,
+							double num_tuples,
+							BlockNumber num_all_visible_pages,
+							TransactionId relfrozenxid,
+							TransactionId relminmxid);
+extern bool get_gtt_relstats(Oid relid, BlockNumber *relpages, double *reltuples,
+							BlockNumber *relallvisible, TransactionId *relfrozenxid,
+							TransactionId *relminmxid);
+extern void gtt_force_enable_index(Relation index);
+extern void gtt_fix_index_state(Relation index);
+extern void init_gtt_storage(CmdType operation, ResultRelInfo *resultRelInfo);
+extern Oid gtt_fetch_current_relfilenode(Oid relid);
+extern void gtt_switch_rel_relfilenode(Oid rel1, Oid relfilenode1, Oid rel2, Oid relfilenode2, bool footprint);
+
+#endif							/* STORAGE_H */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index e2638ab..89a5ce4 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_seq(Relation rel);
 
 #endif							/* SEQUENCE_H */
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index 93f9446..14cafae 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -120,4 +120,7 @@ extern Oid	attnumTypeId(Relation rd, int attid);
 extern Oid	attnumCollationId(Relation rd, int attid);
 extern bool isQueryUsingTempRelation(Query *query);
 
+/* global temp table check */
+extern bool is_query_using_gtt(Query *query);
+
 #endif							/* PARSE_RELATION_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 3f88683..f870e9a 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -405,6 +405,8 @@ do { \
 #define PageClearPrunable(page) \
 	(((PageHeader) (page))->pd_prune_xid = InvalidTransactionId)
 
+#define GlobalTempRelationPageIsNotInitialized(rel, page) \
+	((rel)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP && PageIsNew(page))
 
 /* ----------------------------------------------------------------
  *		extern declarations
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 8fda8e4..172eae9 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -220,6 +220,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_TBM,
 	LWTRANCHE_PARALLEL_APPEND,
 	LWTRANCHE_SXACT,
+	LWTRANCHE_GTT_CTL,
 	LWTRANCHE_FIRST_USER_DEFINED
 }			BuiltinTrancheIds;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index ae4f573..69cce92 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -117,6 +117,8 @@ struct PGPROC
 	Oid			tempNamespaceId;	/* OID of temp schema this backend is
 									 * using */
 
+	TransactionId session_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 a5c7d0c..a86f61f 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -125,4 +125,6 @@ extern void ProcArraySetReplicationSlotXmin(TransactionId xmin,
 extern void ProcArrayGetReplicationSlotXmin(TransactionId *xmin,
 											TransactionId *catalog_xmin);
 
+extern int list_all_session_gtt_frozenxids(int max_size, int *pids, uint32 *xids, int *n);
+
 #endif							/* PROCARRAY_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 2819282..363fc33 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -282,6 +282,10 @@ extern int	tcp_user_timeout;
 extern bool trace_sort;
 #endif
 
+/* global temporary table */
+extern int	max_active_gtt;
+/* end */
+
 /*
  * Functions exported by guc.c
  */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 0b5957b..0720a4c 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -57,7 +57,7 @@ typedef struct RelationData
 	struct SMgrRelationData *rd_smgr;	/* cached file handle, or NULL */
 	int			rd_refcnt;		/* reference count */
 	BackendId	rd_backend;		/* owning backend id, if temporary relation */
-	bool		rd_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
@@ -305,6 +305,7 @@ typedef struct StdRdOptions
 	int			parallel_workers;	/* max number of parallel workers */
 	bool		vacuum_index_cleanup;	/* enables index vacuuming and cleanup */
 	bool		vacuum_truncate;	/* enables vacuum to truncate a relation */
+	bool		on_commit_delete_rows;	/* global temp table */
 } StdRdOptions;
 
 #define HEAP_MIN_FILLFACTOR			10
@@ -570,11 +571,13 @@ typedef struct ViewOptions
  *		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.
  *
@@ -582,6 +585,7 @@ typedef struct ViewOptions
  */
 #define RELATION_IS_LOCAL(relation) \
 	((relation)->rd_islocaltemp || \
+	 (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP || \
 	 (relation)->rd_createSubid != InvalidSubTransactionId)
 
 /*
@@ -594,6 +598,14 @@ typedef struct ViewOptions
 	((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP && \
 	 !(relation)->rd_islocaltemp)
 
+/*
+ * RELATION_IS_TEMP
+ *		Test a rel is either local temp relation of this session
+ * 		or global temp relation.
+ */
+#define RELATION_IS_TEMP(relation) \
+	((relation)->rd_islocaltemp || \
+	 (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 
 /*
  * RelationIsScannable
@@ -637,6 +649,17 @@ typedef struct ViewOptions
 	 RelationNeedsWAL(relation) && \
 	 !IsCatalogRelation(relation))
 
+/* global temp table implementations */
+#define RELATION_IS_GLOBAL_TEMP(relation)	((relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+
+#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)
+
+#define RelationGetRelPersistence(relation) ((relation)->rd_rel->relpersistence)
+
 /* routines in utils/cache/relcache.c */
 extern void RelationIncrementReferenceCount(Relation rel);
 extern void RelationDecrementReferenceCount(Relation rel);
diff --git a/src/test/regress/expected/gtt_clean.out b/src/test/regress/expected/gtt_clean.out
new file mode 100644
index 0000000..ca2d135
--- /dev/null
+++ b/src/test/regress/expected/gtt_clean.out
@@ -0,0 +1,14 @@
+reset search_path;
+select pg_sleep(5);
+ pg_sleep 
+----------
+ 
+(1 row)
+
+drop schema gtt cascade;
+NOTICE:  drop cascades to 5 other objects
+DETAIL:  drop cascades to table gtt.gtt1
+drop cascades to table gtt.gtt2
+drop cascades to table gtt.gtt3
+drop cascades to table gtt.gtt_t_kenyon
+drop cascades to table gtt.gtt_with_seq
diff --git a/src/test/regress/expected/gtt_function.out b/src/test/regress/expected/gtt_function.out
new file mode 100644
index 0000000..d6a675a
--- /dev/null
+++ b/src/test/regress/expected/gtt_function.out
@@ -0,0 +1,383 @@
+CREATE SCHEMA IF NOT EXISTS gtt_function;
+set search_path=gtt_function,sys;
+create global temp table gtt1(a int primary key, b text);
+create global temp table gtt_test_rename(a int primary key, b text);
+create global temp table gtt2(a int primary key, b text) on commit delete rows;
+create global temp table gtt3(a int primary key, b text) on commit PRESERVE rows;
+create global temp table tmp_t0(c0 tsvector,c1 varchar(100));
+create table tbl_inherits_parent(
+a int not null,
+b varchar(32) not null default 'Got u',
+c int check (c > 0),
+d date not null
+);
+create global temp table tbl_inherits_parent_global_temp(
+a int not null,
+b varchar(32) not null default 'Got u',
+c int check (c > 0),
+d date not null
+)on commit delete rows;
+CREATE global temp TABLE products (
+    product_no integer PRIMARY KEY,
+    name text,
+    price numeric
+);
+create global temp table gtt6(n int) with (on_commit_delete_rows='true');
+begin;
+insert into gtt6 values (9);
+-- 1 row
+select * from gtt6;
+ n 
+---
+ 9
+(1 row)
+
+commit;
+-- 0 row
+select * from gtt6;
+ n 
+---
+(0 rows)
+
+-- ERROR
+create index CONCURRENTLY idx_gtt1 on gtt1 (b);
+-- ERROR
+cluster gtt1 using gtt1_pkey;
+-- ERROR
+create table gtt1(a int primary key, b text) on commit delete rows;
+ERROR:  ON COMMIT can only be used on temporary tables
+-- ERROR
+alter table gtt1 SET TABLESPACE pg_default;
+ERROR:  not support alter table set tablespace on global temp table
+-- ERROR
+alter table gtt1 set ( on_commit_delete_rows='true');
+ERROR:  table cannot add or modify on commit parameter by ALTER TABLE command.
+-- ERROR
+create table gtt1(a int primary key, b text) with(on_commit_delete_rows=true);
+ERROR:  The parameter on_commit_delete_rows is exclusive to the global temp table, which cannot be specified by a regular table
+-- ERROR
+create or replace global temp view gtt_v as select 5;
+ERROR:  views cannot be global temp because they do not have storage
+create table foo();
+-- ERROR
+alter table foo set (on_commit_delete_rows='true');
+ERROR:  table cannot add or modify on commit parameter by ALTER TABLE command.
+-- ok
+CREATE global temp TABLE measurement (
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+--ok
+CREATE global temp TABLE p_table01 (
+id        bigserial NOT NULL,
+cre_time  timestamp without time zone,
+note      varchar(30)
+) PARTITION BY RANGE (cre_time)
+WITH (
+OIDS = FALSE
+)on commit delete rows;
+ 
+CREATE global temp TABLE p_table01_2018
+PARTITION OF p_table01
+FOR VALUES FROM ('2018-01-01 00:00:00') TO ('2019-01-01 00:00:00') on commit delete rows;
+ 
+CREATE global temp TABLE p_table01_2017
+PARTITION OF p_table01
+FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2018-01-01 00:00:00') on commit delete rows;
+begin;
+insert into p_table01 values(1,'2018-01-02 00:00:00','test1');
+insert into p_table01 values(1,'2018-01-02 00:00:00','test2');
+select count(*) from p_table01;
+ count 
+-------
+     2
+(1 row)
+
+commit;
+select count(*) from p_table01;
+ count 
+-------
+     0
+(1 row)
+
+--ok
+CREATE global temp TABLE p_table02 (
+id        bigserial NOT NULL,
+cre_time  timestamp without time zone,
+note      varchar(30)
+) PARTITION BY RANGE (cre_time)
+WITH (
+OIDS = FALSE
+)
+on commit PRESERVE rows;
+CREATE global temp TABLE p_table02_2018
+PARTITION OF p_table02
+FOR VALUES FROM ('2018-01-01 00:00:00') TO ('2019-01-01 00:00:00');
+CREATE global temp TABLE p_table02_2017
+PARTITION OF p_table02
+FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2018-01-01 00:00:00');
+-- ERROR
+create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent);
+ERROR:  The parent table must be global temporary table
+-- ok
+create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent_global_temp) on commit delete rows;
+select relname ,relkind, relpersistence, reloptions from pg_class where relname like 'p_table0%' or  relname like 'tbl_inherits%' order by relname;
+             relname             | relkind | relpersistence |          reloptions           
+---------------------------------+---------+----------------+-------------------------------
+ p_table01                       | p       | g              | {on_commit_delete_rows=true}
+ p_table01_2017                  | r       | g              | {on_commit_delete_rows=true}
+ p_table01_2018                  | r       | g              | {on_commit_delete_rows=true}
+ p_table01_id_seq                | S       | g              | 
+ p_table02                       | p       | g              | {on_commit_delete_rows=false}
+ p_table02_2017                  | r       | g              | {on_commit_delete_rows=false}
+ p_table02_2018                  | r       | g              | {on_commit_delete_rows=false}
+ p_table02_id_seq                | S       | g              | 
+ tbl_inherits_parent             | r       | p              | 
+ tbl_inherits_parent_global_temp | r       | g              | {on_commit_delete_rows=true}
+ tbl_inherits_partition          | r       | g              | {on_commit_delete_rows=true}
+(11 rows)
+
+-- ERROR
+create global temp table gtt3(a int primary key, b text) on commit drop;
+ERROR:  global temp table not support on commit drop clause
+-- ERROR
+create global temp table gtt4(a int primary key, b text) with(on_commit_delete_rows=true) on commit delete rows;
+ERROR:  could not create global temporary table with on commit and with clause at same time
+-- ok
+create global temp table gtt5(a int primary key, b text) with(on_commit_delete_rows=true);
+--ok
+alter table gtt_test_rename rename to gtt_test_new;
+-- ok
+ALTER TABLE gtt_test_new ADD COLUMN address varchar(30);
+-- ERROR
+CREATE TABLE orders (
+    order_id integer PRIMARY KEY,
+    product_no integer REFERENCES products (product_no),
+    quantity integer
+);
+ERROR:  constraints on permanent tables may reference only permanent tables
+-- ok
+CREATE global temp TABLE orders (
+    order_id integer PRIMARY KEY,
+    product_no integer REFERENCES products (product_no),
+    quantity integer
+)on commit delete rows;
+--ERROR
+insert into orders values(1,1,1);
+ERROR:  insert or update on table "orders" violates foreign key constraint "orders_product_no_fkey"
+DETAIL:  Key (product_no)=(1) is not present in table "products".
+--ok
+insert into products values(1,'test',1.0);
+begin;
+insert into orders values(1,1,1);
+commit;
+select count(*) from products;
+ count 
+-------
+     1
+(1 row)
+
+select count(*) from orders;
+ count 
+-------
+     0
+(1 row)
+
+-- ok
+CREATE GLOBAL TEMPORARY TABLE mytable (
+  id SERIAL PRIMARY KEY,
+  data text
+) on commit preserve rows;
+-- ok
+create global temp table gtt_seq(id int GENERATED ALWAYS AS IDENTITY (START WITH 2) primary key, a int)  on commit PRESERVE rows;
+insert into gtt_seq (a) values(1);
+insert into gtt_seq (a) values(2);
+select * from gtt_seq order by id;
+ id | a 
+----+---
+  2 | 1
+  3 | 2
+(2 rows)
+
+truncate gtt_seq;
+select * from gtt_seq order by id;
+ id | a 
+----+---
+(0 rows)
+
+insert into gtt_seq (a) values(3);
+select * from gtt_seq order by id;
+ id | a 
+----+---
+  4 | 3
+(1 row)
+
+--ERROR
+CREATE MATERIALIZED VIEW mv_gtt1 as select * from gtt1;
+ERROR:  materialized views must not use global temporary tables or views
+-- ok
+create index idx_gtt1_1 on gtt1 using hash (a);
+create index idx_tmp_t0_1 on tmp_t0 using gin (c0);
+create index idx_tmp_t0_2 on tmp_t0 using gist (c0);
+--ok
+create global temp table gt (a SERIAL,b int);
+begin;
+set transaction_read_only = true;
+insert into gt (b) values(1);
+select * from gt;
+ a | b 
+---+---
+ 1 | 1
+(1 row)
+
+commit;
+create sequence seq_1;
+CREATE GLOBAL TEMPORARY TABLE gtt_s_1(c1 int PRIMARY KEY) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMPORARY TABLE gtt_s_2(c1 int PRIMARY KEY) ON COMMIT PRESERVE ROWS;
+alter table gtt_s_1 add c2 int default nextval('seq_1');
+alter table gtt_s_2 add c2 int default nextval('seq_1');
+begin;
+insert into gtt_s_1 (c1)values(1);
+insert into gtt_s_2 (c1)values(1);
+insert into gtt_s_1 (c1)values(2);
+insert into gtt_s_2 (c1)values(2);
+select * from gtt_s_1 order by c1;
+ c1 | c2 
+----+----
+  1 |  1
+  2 |  3
+(2 rows)
+
+commit;
+select * from gtt_s_1 order by c1;
+ c1 | c2 
+----+----
+(0 rows)
+
+select * from gtt_s_2 order by c1;
+ c1 | c2 
+----+----
+  1 |  2
+  2 |  4
+(2 rows)
+
+--ok
+create global temp table gt1(a int);
+insert into gt1 values(generate_series(1,100000));
+create index idx_gt1_1 on gt1 (a);
+create index idx_gt1_2 on gt1((a + 1));
+create index idx_gt1_3 on gt1((a*10),(a+a),(a-1));
+explain (costs off) select * from gt1 where a=1;
+              QUERY PLAN              
+--------------------------------------
+ Bitmap Heap Scan on gt1
+   Recheck Cond: (a = 1)
+   ->  Bitmap Index Scan on idx_gt1_1
+         Index Cond: (a = 1)
+(4 rows)
+
+explain (costs off) select * from gt1 where a=200000;
+              QUERY PLAN              
+--------------------------------------
+ Bitmap Heap Scan on gt1
+   Recheck Cond: (a = 200000)
+   ->  Bitmap Index Scan on idx_gt1_1
+         Index Cond: (a = 200000)
+(4 rows)
+
+explain (costs off) select * from gt1 where a*10=300;
+              QUERY PLAN              
+--------------------------------------
+ Bitmap Heap Scan on gt1
+   Recheck Cond: ((a * 10) = 300)
+   ->  Bitmap Index Scan on idx_gt1_3
+         Index Cond: ((a * 10) = 300)
+(4 rows)
+
+explain (costs off) select * from gt1 where a*10=3;
+              QUERY PLAN              
+--------------------------------------
+ Bitmap Heap Scan on gt1
+   Recheck Cond: ((a * 10) = 3)
+   ->  Bitmap Index Scan on idx_gt1_3
+         Index Cond: ((a * 10) = 3)
+(4 rows)
+
+analyze gt1;
+explain (costs off) select * from gt1 where a=1;
+               QUERY PLAN               
+----------------------------------------
+ Index Only Scan using idx_gt1_1 on gt1
+   Index Cond: (a = 1)
+(2 rows)
+
+explain (costs off) select * from gt1 where a=200000;
+               QUERY PLAN               
+----------------------------------------
+ Index Only Scan using idx_gt1_1 on gt1
+   Index Cond: (a = 200000)
+(2 rows)
+
+explain (costs off) select * from gt1 where a*10=300;
+            QUERY PLAN             
+-----------------------------------
+ Index Scan using idx_gt1_3 on gt1
+   Index Cond: ((a * 10) = 300)
+(2 rows)
+
+explain (costs off) select * from gt1 where a*10=3;
+            QUERY PLAN             
+-----------------------------------
+ Index Scan using idx_gt1_3 on gt1
+   Index Cond: ((a * 10) = 3)
+(2 rows)
+
+--ok
+create global temp table gtt_test1(c1 int) with(on_commit_delete_rows='1');
+create global temp table gtt_test2(c1 int) with(on_commit_delete_rows='0');
+create global temp table gtt_test3(c1 int) with(on_commit_delete_rows='t');
+create global temp table gtt_test4(c1 int) with(on_commit_delete_rows='f');
+create global temp table gtt_test5(c1 int) with(on_commit_delete_rows='yes');
+create global temp table gtt_test6(c1 int) with(on_commit_delete_rows='no');
+create global temp table gtt_test7(c1 int) with(on_commit_delete_rows='y');
+create global temp table gtt_test8(c1 int) with(on_commit_delete_rows='n');
+--error
+create global temp table gtt_test9(c1 int) with(on_commit_delete_rows='tr');
+create global temp table gtt_test10(c1 int) with(on_commit_delete_rows='ye');
+reset search_path;
+drop schema gtt_function cascade;
+NOTICE:  drop cascades to 33 other objects
+DETAIL:  drop cascades to table gtt_function.gtt1
+drop cascades to table gtt_function.gtt_test_new
+drop cascades to table gtt_function.gtt2
+drop cascades to table gtt_function.gtt3
+drop cascades to table gtt_function.tmp_t0
+drop cascades to table gtt_function.tbl_inherits_parent
+drop cascades to table gtt_function.tbl_inherits_parent_global_temp
+drop cascades to table gtt_function.products
+drop cascades to table gtt_function.gtt6
+drop cascades to table gtt_function.foo
+drop cascades to table gtt_function.measurement
+drop cascades to table gtt_function.p_table01
+drop cascades to table gtt_function.p_table02
+drop cascades to table gtt_function.tbl_inherits_partition
+drop cascades to table gtt_function.gtt5
+drop cascades to table gtt_function.orders
+drop cascades to table gtt_function.mytable
+drop cascades to table gtt_function.gtt_seq
+drop cascades to table gtt_function.gt
+drop cascades to sequence gtt_function.seq_1
+drop cascades to table gtt_function.gtt_s_1
+drop cascades to table gtt_function.gtt_s_2
+drop cascades to table gtt_function.gt1
+drop cascades to table gtt_function.gtt_test1
+drop cascades to table gtt_function.gtt_test2
+drop cascades to table gtt_function.gtt_test3
+drop cascades to table gtt_function.gtt_test4
+drop cascades to table gtt_function.gtt_test5
+drop cascades to table gtt_function.gtt_test6
+drop cascades to table gtt_function.gtt_test7
+drop cascades to table gtt_function.gtt_test8
+drop cascades to table gtt_function.gtt_test9
+drop cascades to table gtt_function.gtt_test10
diff --git a/src/test/regress/expected/gtt_parallel_1.out b/src/test/regress/expected/gtt_parallel_1.out
new file mode 100644
index 0000000..0646aae
--- /dev/null
+++ b/src/test/regress/expected/gtt_parallel_1.out
@@ -0,0 +1,90 @@
+set search_path=gtt,sys;
+select nextval('gtt_with_seq_c2_seq');
+ nextval 
+---------
+       1
+(1 row)
+
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+ a | b 
+---+---
+(0 rows)
+
+begin;
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+(1 row)
+
+commit;
+select * from gtt1 order by a;
+ a | b 
+---+---
+(0 rows)
+
+begin;
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+(1 row)
+
+rollback;
+select * from gtt1 order by a;
+ a | b 
+---+---
+(0 rows)
+
+truncate gtt1;
+select * from gtt1 order by a;
+ a | b 
+---+---
+(0 rows)
+
+begin;
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+(1 row)
+
+truncate gtt1;
+select * from gtt1 order by a;
+ a | b 
+---+---
+(0 rows)
+
+insert into gtt1 values(1, 'test1');
+rollback;
+select * from gtt1 order by a;
+ a | b 
+---+---
+(0 rows)
+
+begin;
+select * from gtt1 order by a;
+ a | b 
+---+---
+(0 rows)
+
+truncate gtt1;
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+(1 row)
+
+truncate gtt1;
+commit;
+select * from gtt1 order by a;
+ a | b 
+---+---
+(0 rows)
+
+reset search_path;
diff --git a/src/test/regress/expected/gtt_parallel_2.out b/src/test/regress/expected/gtt_parallel_2.out
new file mode 100644
index 0000000..2f084be
--- /dev/null
+++ b/src/test/regress/expected/gtt_parallel_2.out
@@ -0,0 +1,343 @@
+set search_path=gtt,sys;
+insert into gtt3 values(1, 'test1');
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+(1 row)
+
+begin;
+insert into gtt3 values(2, 'test1');
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+ 2 | test1
+(2 rows)
+
+commit;
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+ 2 | test1
+(2 rows)
+
+begin;
+insert into gtt3 values(3, 'test1');
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+ 2 | test1
+ 3 | test1
+(3 rows)
+
+rollback;
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+ 2 | test1
+(2 rows)
+
+truncate gtt3;
+select * from gtt3 order by a;
+ a | b 
+---+---
+(0 rows)
+
+insert into gtt3 values(1, 'test1');
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+(1 row)
+
+begin;
+insert into gtt3 values(2, 'test2');
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+ 2 | test2
+(2 rows)
+
+truncate gtt3;
+select * from gtt3 order by a;
+ a | b 
+---+---
+(0 rows)
+
+insert into gtt3 values(3, 'test3');
+update gtt3 set a = 3 where b = 'test1';
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 3 | test3
+(1 row)
+
+rollback;
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+(1 row)
+
+begin;
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 1 | test1
+(1 row)
+
+truncate gtt3;
+insert into gtt3 values(5, 'test5');
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 5 | test5
+(1 row)
+
+truncate gtt3;
+insert into gtt3 values(6, 'test6');
+commit;
+select * from gtt3 order by a;
+ a |   b   
+---+-------
+ 6 | test6
+(1 row)
+
+truncate gtt3;
+insert into gtt3 values(1);
+select * from gtt3;
+ a | b 
+---+---
+ 1 | 
+(1 row)
+
+begin;
+insert into gtt3 values(2);
+select * from gtt3;
+ a | b 
+---+---
+ 1 | 
+ 2 | 
+(2 rows)
+
+SAVEPOINT save1;
+truncate gtt3;
+insert into gtt3 values(3);
+select * from gtt3;
+ a | b 
+---+---
+ 3 | 
+(1 row)
+
+SAVEPOINT save2;
+truncate gtt3;
+insert into gtt3 values(4);
+select * from gtt3;
+ a | b 
+---+---
+ 4 | 
+(1 row)
+
+SAVEPOINT save3;
+rollback to savepoint save2;
+Select * from gtt3;
+ a | b 
+---+---
+ 3 | 
+(1 row)
+
+insert into gtt3 values(5);
+select * from gtt3;
+ a | b 
+---+---
+ 3 | 
+ 5 | 
+(2 rows)
+
+rollback;
+select * from gtt3;
+ a | b 
+---+---
+ 1 | 
+(1 row)
+
+truncate gtt3;
+insert into gtt3 values(1);
+select * from gtt3;
+ a | b 
+---+---
+ 1 | 
+(1 row)
+
+begin;
+insert into gtt3 values(2);
+select * from gtt3;
+ a | b 
+---+---
+ 1 | 
+ 2 | 
+(2 rows)
+
+SAVEPOINT save1;
+truncate gtt3;
+insert into gtt3 values(3);
+select * from gtt3;
+ a | b 
+---+---
+ 3 | 
+(1 row)
+
+SAVEPOINT save2;
+truncate gtt3;
+insert into gtt3 values(4);
+select * from gtt3;
+ a | b 
+---+---
+ 4 | 
+(1 row)
+
+SAVEPOINT save3;
+rollback to savepoint save2;
+Select * from gtt3;
+ a | b 
+---+---
+ 3 | 
+(1 row)
+
+insert into gtt3 values(5);
+select * from gtt3;
+ a | b 
+---+---
+ 3 | 
+ 5 | 
+(2 rows)
+
+commit;
+select * from gtt3;
+ a | b 
+---+---
+ 3 | 
+ 5 | 
+(2 rows)
+
+truncate gtt3;
+insert into gtt3 values(generate_series(1,100000), 'testing');
+select count(*) from gtt3;
+ count  
+--------
+ 100000
+(1 row)
+
+analyze gtt3;
+explain (COSTS FALSE) select * from gtt3 where a =300;
+             QUERY PLAN             
+------------------------------------
+ Index Scan using gtt3_pkey on gtt3
+   Index Cond: (a = 300)
+(2 rows)
+
+insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500);
+insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',2000);
+insert into gtt_t_kenyon select generate_series(3,4),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',4000);
+insert into gtt_t_kenyon select generate_series(5,6),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',5500);
+select relname, pg_relation_size(oid),pg_relation_size(reltoastrelid),pg_table_size(oid),pg_indexes_size(oid),pg_total_relation_size(oid) from pg_class where relname = 'gtt_t_kenyon';
+   relname    | pg_relation_size | pg_relation_size | pg_table_size | pg_indexes_size | pg_total_relation_size 
+--------------+------------------+------------------+---------------+-----------------+------------------------
+ gtt_t_kenyon |           450560 |             8192 |        499712 |          114688 |                 614400
+(1 row)
+
+select relname from pg_class where relname = 'gtt_t_kenyon' and reltoastrelid != 0;
+   relname    
+--------------
+ gtt_t_kenyon
+(1 row)
+
+select
+c.relname, pg_relation_size(c.oid),pg_table_size(c.oid),pg_total_relation_size(c.oid)
+from
+pg_class c
+where
+c.oid in
+(
+select
+i.indexrelid as indexrelid
+from 
+pg_index i ,pg_class cc
+where cc.relname = 'gtt_t_kenyon' and cc.oid = i.indrelid
+)
+order by c.relname;
+      relname       | pg_relation_size | pg_table_size | pg_total_relation_size 
+--------------------+------------------+---------------+------------------------
+ idx_gtt_t_kenyon_1 |            65536 |         65536 |                  65536
+ idx_gtt_t_kenyon_2 |            49152 |         49152 |                  49152
+(2 rows)
+
+select count(*) from gtt_t_kenyon;
+ count 
+-------
+  2006
+(1 row)
+
+begin;
+cluster gtt_t_kenyon using idx_gtt_t_kenyon_1;
+commit;
+select count(*) from gtt_t_kenyon;
+ count 
+-------
+  2006
+(1 row)
+
+begin;
+cluster gtt_t_kenyon using idx_gtt_t_kenyon_1;
+rollback;
+select count(*) from gtt_t_kenyon;
+ count 
+-------
+  2006
+(1 row)
+
+vacuum full gtt_t_kenyon;
+select count(*) from gtt_t_kenyon;
+ count 
+-------
+  2006
+(1 row)
+
+begin;
+truncate gtt_t_kenyon;
+insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500);
+cluster gtt_t_kenyon using idx_gtt_t_kenyon_1;
+commit;
+select count(*) from gtt_t_kenyon;
+ count 
+-------
+  2000
+(1 row)
+
+insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',2000);
+insert into gtt_t_kenyon select generate_series(3,4),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',4000);
+insert into gtt_t_kenyon select generate_series(5,6),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',5500);
+begin;
+truncate gtt_t_kenyon;
+insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500);
+cluster gtt_t_kenyon using idx_gtt_t_kenyon_1;
+rollback;
+select count(*) from gtt_t_kenyon;
+ count 
+-------
+  2006
+(1 row)
+
+insert into gtt_with_seq (c1) values(1);
+select * from gtt_with_seq;
+ c1 | c2 
+----+----
+  1 |  1
+(1 row)
+
+reset search_path;
diff --git a/src/test/regress/expected/gtt_prepare.out b/src/test/regress/expected/gtt_prepare.out
new file mode 100644
index 0000000..8c0c376
--- /dev/null
+++ b/src/test/regress/expected/gtt_prepare.out
@@ -0,0 +1,10 @@
+CREATE SCHEMA IF NOT EXISTS gtt;
+set search_path=gtt,sys;
+create global temp table gtt1(a int primary key, b text) on commit delete rows;
+create global temp table gtt2(a int primary key, b text) on commit delete rows;
+create global temp table gtt3(a int primary key, b text);
+create global temp table gtt_t_kenyon(id int,vname varchar(48),remark text) on commit PRESERVE rows;
+create index idx_gtt_t_kenyon_1 on gtt_t_kenyon(id);
+create index idx_gtt_t_kenyon_2 on gtt_t_kenyon(remark);
+CREATE GLOBAL TEMPORARY TABLE gtt_with_seq(c1 bigint, c2 bigserial) on commit PRESERVE rows;
+reset search_path;
diff --git a/src/test/regress/expected/gtt_stats.out b/src/test/regress/expected/gtt_stats.out
new file mode 100644
index 0000000..4420fdb
--- /dev/null
+++ b/src/test/regress/expected/gtt_stats.out
@@ -0,0 +1,80 @@
+CREATE SCHEMA IF NOT EXISTS gtt_stats;
+set search_path=gtt_stats,sys;
+-- expect 0
+select count(*) from pg_gtt_attached_pids;
+ count 
+-------
+     0
+(1 row)
+
+-- expect 0
+select count(*) from pg_list_gtt_relfrozenxids();
+ count 
+-------
+     0
+(1 row)
+
+create global temp table gtt_stats.gtt(a int primary key, b text) on commit PRESERVE rows;
+-- expect 0
+select count(*) from pg_gtt_attached_pids;
+ count 
+-------
+     1
+(1 row)
+
+-- expect 0
+select count(*) from pg_list_gtt_relfrozenxids();
+ count 
+-------
+     2
+(1 row)
+
+insert into gtt values(generate_series(1,10000),'test');
+-- expect 1
+select count(*) from pg_gtt_attached_pids;
+ count 
+-------
+     1
+(1 row)
+
+-- expect 2
+select count(*) from pg_list_gtt_relfrozenxids();
+ count 
+-------
+     2
+(1 row)
+
+-- expect 2
+select schemaname, tablename, relpages, reltuples, relallvisible from pg_gtt_relstats where schemaname = 'gtt_stats' order by tablename;
+ schemaname | tablename | relpages | reltuples | relallvisible 
+------------+-----------+----------+-----------+---------------
+ gtt_stats  | gtt       |        0 |         0 |             0
+ gtt_stats  | gtt_pkey  |        1 |         0 |             0
+(2 rows)
+
+-- expect 0
+select * from pg_gtt_stats order by tablename;
+ schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram 
+------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+------------------+-------------+-------------------+------------------------+----------------------
+(0 rows)
+
+reindex table gtt;
+reindex index gtt_pkey;
+analyze gtt;
+select schemaname, tablename, relpages, reltuples, relallvisible from pg_gtt_relstats where schemaname = 'gtt_stats' order by tablename;
+ schemaname | tablename | relpages | reltuples | relallvisible 
+------------+-----------+----------+-----------+---------------
+ gtt_stats  | gtt       |       55 |     10000 |             0
+ gtt_stats  | gtt_pkey  |       30 |     10000 |             0
+(2 rows)
+
+select * from pg_gtt_stats order by tablename;
+ schemaname | tablename | attname | inherited | null_frac | avg_width | n_distinct | most_common_vals | most_common_freqs |                                                                                                                                                                                                                                                histogram_bounds                                                                                                                                                                                                                                                 | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram 
+------------+-----------+---------+-----------+-----------+-----------+------------+------------------+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------+------------------------+----------------------
+ gtt_stats  | gtt       | a       | f         |         0 |         4 |         -1 |                  |                   | {1,100,200,300,400,500,600,700,800,900,1000,1100,1200,1300,1400,1500,1600,1700,1800,1900,2000,2100,2200,2300,2400,2500,2600,2700,2800,2900,3000,3100,3200,3300,3400,3500,3600,3700,3800,3900,4000,4100,4200,4300,4400,4500,4600,4700,4800,4900,5000,5100,5200,5300,5400,5500,5600,5700,5800,5900,6000,6100,6200,6300,6400,6500,6600,6700,6800,6900,7000,7100,7200,7300,7400,7500,7600,7700,7800,7900,8000,8100,8200,8300,8400,8500,8600,8700,8800,8900,9000,9100,9200,9300,9400,9500,9600,9700,9800,9900,10000} |           1 |                   |                        | 
+ gtt_stats  | gtt       | b       | f         |         0 |         5 |          1 | {test}           | {1}               |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |           1 |                   |                        | 
+(2 rows)
+
+reset search_path;
+drop schema gtt_stats cascade;
+NOTICE:  drop cascades to table gtt_stats.gtt
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index ac31840..55b221e 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1349,6 +1349,94 @@ pg_group| SELECT pg_authid.rolname AS groname,
           WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
    FROM pg_authid
   WHERE (NOT pg_authid.rolcanlogin);
+pg_gtt_attached_pids| SELECT n.nspname AS schemaname,
+    c.relname AS tablename,
+    s.relid,
+    s.pid
+   FROM (pg_class c
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))),
+    LATERAL pg_gtt_attached_pid(c.oid) s(relid, pid)
+  WHERE ((c.relpersistence = 'g'::"char") AND (c.relkind = ANY (ARRAY['r'::"char", 'S'::"char"])) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_gtt_relstats| SELECT n.nspname AS schemaname,
+    c.relname AS tablename,
+    s.relfilenode,
+    s.relpages,
+    s.reltuples,
+    s.relallvisible,
+    s.relfrozenxid,
+    s.relminmxid
+   FROM (pg_class c
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))),
+    LATERAL pg_get_gtt_relstats(c.oid) s(relfilenode, relpages, reltuples, relallvisible, relfrozenxid, relminmxid)
+  WHERE ((c.relpersistence = 'g'::"char") AND (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char", 'i'::"char", 't'::"char"])) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_gtt_stats| 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
+            ELSE NULL::text[]
+        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
+            ELSE NULL::real[]
+        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
+            ELSE NULL::text[]
+        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]
+            ELSE NULL::real
+        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
+            ELSE NULL::text[]
+        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
+            ELSE NULL::real[]
+        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
+            ELSE NULL::real[]
+        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))),
+    LATERAL pg_get_gtt_statistics(c.oid, (a.attnum)::integer, ''::text) s(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)
+  WHERE ((c.relpersistence = 'g'::"char") AND (c.relkind = ANY (ARRAY['r'::"char", 'p'::"char", 'i'::"char", 't'::"char"])) 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))));
 pg_hba_file_rules| SELECT a.line_number,
     a.type,
     a.database,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 95f1925..76b2374 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -121,3 +121,10 @@ test: fast_default
 
 # run stats by itself because its delay may be insufficient under heavy load
 test: stats
+
+# global temp table test
+test: gtt_stats
+test: gtt_function
+test: gtt_prepare
+test: gtt_parallel_1 gtt_parallel_2
+test: gtt_clean
diff --git a/src/test/regress/sql/gtt_clean.sql b/src/test/regress/sql/gtt_clean.sql
new file mode 100644
index 0000000..2c8e586
--- /dev/null
+++ b/src/test/regress/sql/gtt_clean.sql
@@ -0,0 +1,8 @@
+
+
+reset search_path;
+
+select pg_sleep(5);
+
+drop schema gtt cascade;
+
diff --git a/src/test/regress/sql/gtt_function.sql b/src/test/regress/sql/gtt_function.sql
new file mode 100644
index 0000000..81f0bfc
--- /dev/null
+++ b/src/test/regress/sql/gtt_function.sql
@@ -0,0 +1,253 @@
+
+CREATE SCHEMA IF NOT EXISTS gtt_function;
+
+set search_path=gtt_function,sys;
+
+create global temp table gtt1(a int primary key, b text);
+
+create global temp table gtt_test_rename(a int primary key, b text);
+
+create global temp table gtt2(a int primary key, b text) on commit delete rows;
+
+create global temp table gtt3(a int primary key, b text) on commit PRESERVE rows;
+
+create global temp table tmp_t0(c0 tsvector,c1 varchar(100));
+
+create table tbl_inherits_parent(
+a int not null,
+b varchar(32) not null default 'Got u',
+c int check (c > 0),
+d date not null
+);
+
+create global temp table tbl_inherits_parent_global_temp(
+a int not null,
+b varchar(32) not null default 'Got u',
+c int check (c > 0),
+d date not null
+)on commit delete rows;
+
+CREATE global temp TABLE products (
+    product_no integer PRIMARY KEY,
+    name text,
+    price numeric
+);
+
+create global temp table gtt6(n int) with (on_commit_delete_rows='true');
+
+begin;
+insert into gtt6 values (9);
+-- 1 row
+select * from gtt6;
+commit;
+-- 0 row
+select * from gtt6;
+
+-- ERROR
+create index CONCURRENTLY idx_gtt1 on gtt1 (b);
+
+-- ERROR
+cluster gtt1 using gtt1_pkey;
+
+-- ERROR
+create table gtt1(a int primary key, b text) on commit delete rows;
+
+-- ERROR
+alter table gtt1 SET TABLESPACE pg_default;
+
+-- ERROR
+alter table gtt1 set ( on_commit_delete_rows='true');
+
+-- ERROR
+create table gtt1(a int primary key, b text) with(on_commit_delete_rows=true);
+
+-- ERROR
+create or replace global temp view gtt_v as select 5;
+
+create table foo();
+-- ERROR
+alter table foo set (on_commit_delete_rows='true');
+
+-- ok
+CREATE global temp TABLE measurement (
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+
+--ok
+CREATE global temp TABLE p_table01 (
+id        bigserial NOT NULL,
+cre_time  timestamp without time zone,
+note      varchar(30)
+) PARTITION BY RANGE (cre_time)
+WITH (
+OIDS = FALSE
+)on commit delete rows;
+ 
+CREATE global temp TABLE p_table01_2018
+PARTITION OF p_table01
+FOR VALUES FROM ('2018-01-01 00:00:00') TO ('2019-01-01 00:00:00') on commit delete rows;
+ 
+CREATE global temp TABLE p_table01_2017
+PARTITION OF p_table01
+FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2018-01-01 00:00:00') on commit delete rows;
+
+begin;
+insert into p_table01 values(1,'2018-01-02 00:00:00','test1');
+insert into p_table01 values(1,'2018-01-02 00:00:00','test2');
+select count(*) from p_table01;
+commit;
+
+select count(*) from p_table01;
+
+--ok
+CREATE global temp TABLE p_table02 (
+id        bigserial NOT NULL,
+cre_time  timestamp without time zone,
+note      varchar(30)
+) PARTITION BY RANGE (cre_time)
+WITH (
+OIDS = FALSE
+)
+on commit PRESERVE rows;
+
+CREATE global temp TABLE p_table02_2018
+PARTITION OF p_table02
+FOR VALUES FROM ('2018-01-01 00:00:00') TO ('2019-01-01 00:00:00');
+
+CREATE global temp TABLE p_table02_2017
+PARTITION OF p_table02
+FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2018-01-01 00:00:00');
+
+-- ERROR
+create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent);
+
+-- ok
+create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent_global_temp) on commit delete rows;
+
+select relname ,relkind, relpersistence, reloptions from pg_class where relname like 'p_table0%' or  relname like 'tbl_inherits%' order by relname;
+
+-- ERROR
+create global temp table gtt3(a int primary key, b text) on commit drop;
+
+-- ERROR
+create global temp table gtt4(a int primary key, b text) with(on_commit_delete_rows=true) on commit delete rows;
+
+-- ok
+create global temp table gtt5(a int primary key, b text) with(on_commit_delete_rows=true);
+
+--ok
+alter table gtt_test_rename rename to gtt_test_new;
+
+-- ok
+ALTER TABLE gtt_test_new ADD COLUMN address varchar(30);
+
+-- ERROR
+CREATE TABLE orders (
+    order_id integer PRIMARY KEY,
+    product_no integer REFERENCES products (product_no),
+    quantity integer
+);
+
+-- ok
+CREATE global temp TABLE orders (
+    order_id integer PRIMARY KEY,
+    product_no integer REFERENCES products (product_no),
+    quantity integer
+)on commit delete rows;
+
+--ERROR
+insert into orders values(1,1,1);
+
+--ok
+insert into products values(1,'test',1.0);
+
+begin;
+insert into orders values(1,1,1);
+commit;
+
+select count(*) from products;
+select count(*) from orders;
+
+-- ok
+CREATE GLOBAL TEMPORARY TABLE mytable (
+  id SERIAL PRIMARY KEY,
+  data text
+) on commit preserve rows;
+
+-- ok
+create global temp table gtt_seq(id int GENERATED ALWAYS AS IDENTITY (START WITH 2) primary key, a int)  on commit PRESERVE rows;
+insert into gtt_seq (a) values(1);
+insert into gtt_seq (a) values(2);
+select * from gtt_seq order by id;
+truncate gtt_seq;
+select * from gtt_seq order by id;
+insert into gtt_seq (a) values(3);
+select * from gtt_seq order by id;
+
+--ERROR
+CREATE MATERIALIZED VIEW mv_gtt1 as select * from gtt1;
+
+-- ok
+create index idx_gtt1_1 on gtt1 using hash (a);
+create index idx_tmp_t0_1 on tmp_t0 using gin (c0);
+create index idx_tmp_t0_2 on tmp_t0 using gist (c0);
+
+--ok
+create global temp table gt (a SERIAL,b int);
+begin;
+set transaction_read_only = true;
+insert into gt (b) values(1);
+select * from gt;
+commit;
+
+create sequence seq_1;
+CREATE GLOBAL TEMPORARY TABLE gtt_s_1(c1 int PRIMARY KEY) ON COMMIT DELETE ROWS;
+CREATE GLOBAL TEMPORARY TABLE gtt_s_2(c1 int PRIMARY KEY) ON COMMIT PRESERVE ROWS;
+alter table gtt_s_1 add c2 int default nextval('seq_1');
+alter table gtt_s_2 add c2 int default nextval('seq_1');
+begin;
+insert into gtt_s_1 (c1)values(1);
+insert into gtt_s_2 (c1)values(1);
+insert into gtt_s_1 (c1)values(2);
+insert into gtt_s_2 (c1)values(2);
+select * from gtt_s_1 order by c1;
+commit;
+select * from gtt_s_1 order by c1;
+select * from gtt_s_2 order by c1;
+
+--ok
+create global temp table gt1(a int);
+insert into gt1 values(generate_series(1,100000));
+create index idx_gt1_1 on gt1 (a);
+create index idx_gt1_2 on gt1((a + 1));
+create index idx_gt1_3 on gt1((a*10),(a+a),(a-1));
+explain (costs off) select * from gt1 where a=1;
+explain (costs off) select * from gt1 where a=200000;
+explain (costs off) select * from gt1 where a*10=300;
+explain (costs off) select * from gt1 where a*10=3;
+analyze gt1;
+explain (costs off) select * from gt1 where a=1;
+explain (costs off) select * from gt1 where a=200000;
+explain (costs off) select * from gt1 where a*10=300;
+explain (costs off) select * from gt1 where a*10=3;
+
+--ok
+create global temp table gtt_test1(c1 int) with(on_commit_delete_rows='1');
+create global temp table gtt_test2(c1 int) with(on_commit_delete_rows='0');
+create global temp table gtt_test3(c1 int) with(on_commit_delete_rows='t');
+create global temp table gtt_test4(c1 int) with(on_commit_delete_rows='f');
+create global temp table gtt_test5(c1 int) with(on_commit_delete_rows='yes');
+create global temp table gtt_test6(c1 int) with(on_commit_delete_rows='no');
+create global temp table gtt_test7(c1 int) with(on_commit_delete_rows='y');
+create global temp table gtt_test8(c1 int) with(on_commit_delete_rows='n');
+
+--error
+create global temp table gtt_test9(c1 int) with(on_commit_delete_rows='tr');
+create global temp table gtt_test10(c1 int) with(on_commit_delete_rows='ye');
+
+reset search_path;
+
+drop schema gtt_function cascade;
+
diff --git a/src/test/regress/sql/gtt_parallel_1.sql b/src/test/regress/sql/gtt_parallel_1.sql
new file mode 100644
index 0000000..d05745e
--- /dev/null
+++ b/src/test/regress/sql/gtt_parallel_1.sql
@@ -0,0 +1,44 @@
+
+
+set search_path=gtt,sys;
+
+select nextval('gtt_with_seq_c2_seq');
+
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+
+begin;
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+commit;
+select * from gtt1 order by a;
+
+begin;
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+rollback;
+select * from gtt1 order by a;
+
+truncate gtt1;
+select * from gtt1 order by a;
+
+begin;
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+truncate gtt1;
+select * from gtt1 order by a;
+insert into gtt1 values(1, 'test1');
+rollback;
+select * from gtt1 order by a;
+
+begin;
+select * from gtt1 order by a;
+truncate gtt1;
+insert into gtt1 values(1, 'test1');
+select * from gtt1 order by a;
+truncate gtt1;
+commit;
+select * from gtt1 order by a;
+
+reset search_path;
+
diff --git a/src/test/regress/sql/gtt_parallel_2.sql b/src/test/regress/sql/gtt_parallel_2.sql
new file mode 100644
index 0000000..39cca5e
--- /dev/null
+++ b/src/test/regress/sql/gtt_parallel_2.sql
@@ -0,0 +1,154 @@
+
+
+set search_path=gtt,sys;
+
+insert into gtt3 values(1, 'test1');
+select * from gtt3 order by a;
+
+begin;
+insert into gtt3 values(2, 'test1');
+select * from gtt3 order by a;
+commit;
+select * from gtt3 order by a;
+
+begin;
+insert into gtt3 values(3, 'test1');
+select * from gtt3 order by a;
+rollback;
+select * from gtt3 order by a;
+
+truncate gtt3;
+select * from gtt3 order by a;
+
+insert into gtt3 values(1, 'test1');
+select * from gtt3 order by a;
+
+begin;
+insert into gtt3 values(2, 'test2');
+select * from gtt3 order by a;
+truncate gtt3;
+select * from gtt3 order by a;
+insert into gtt3 values(3, 'test3');
+update gtt3 set a = 3 where b = 'test1';
+select * from gtt3 order by a;
+rollback;
+select * from gtt3 order by a;
+
+begin;
+select * from gtt3 order by a;
+truncate gtt3;
+insert into gtt3 values(5, 'test5');
+select * from gtt3 order by a;
+truncate gtt3;
+insert into gtt3 values(6, 'test6');
+commit;
+select * from gtt3 order by a;
+
+truncate gtt3;
+insert into gtt3 values(1);
+select * from gtt3;
+begin;
+insert into gtt3 values(2);
+select * from gtt3;
+SAVEPOINT save1;
+truncate gtt3;
+insert into gtt3 values(3);
+select * from gtt3;
+SAVEPOINT save2;
+truncate gtt3;
+insert into gtt3 values(4);
+select * from gtt3;
+SAVEPOINT save3;
+rollback to savepoint save2;
+Select * from gtt3;
+insert into gtt3 values(5);
+select * from gtt3;
+rollback;
+select * from gtt3;
+
+truncate gtt3;
+insert into gtt3 values(1);
+select * from gtt3;
+begin;
+insert into gtt3 values(2);
+select * from gtt3;
+SAVEPOINT save1;
+truncate gtt3;
+insert into gtt3 values(3);
+select * from gtt3;
+SAVEPOINT save2;
+truncate gtt3;
+insert into gtt3 values(4);
+select * from gtt3;
+SAVEPOINT save3;
+rollback to savepoint save2;
+Select * from gtt3;
+insert into gtt3 values(5);
+select * from gtt3;
+commit;
+select * from gtt3;
+
+truncate gtt3;
+insert into gtt3 values(generate_series(1,100000), 'testing');
+select count(*) from gtt3;
+analyze gtt3;
+explain (COSTS FALSE) select * from gtt3 where a =300;
+
+insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500);
+insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',2000);
+insert into gtt_t_kenyon select generate_series(3,4),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',4000);
+insert into gtt_t_kenyon select generate_series(5,6),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',5500);
+select relname, pg_relation_size(oid),pg_relation_size(reltoastrelid),pg_table_size(oid),pg_indexes_size(oid),pg_total_relation_size(oid) from pg_class where relname = 'gtt_t_kenyon';
+select relname from pg_class where relname = 'gtt_t_kenyon' and reltoastrelid != 0;
+
+select
+c.relname, pg_relation_size(c.oid),pg_table_size(c.oid),pg_total_relation_size(c.oid)
+from
+pg_class c
+where
+c.oid in
+(
+select
+i.indexrelid as indexrelid
+from 
+pg_index i ,pg_class cc
+where cc.relname = 'gtt_t_kenyon' and cc.oid = i.indrelid
+)
+order by c.relname;
+
+select count(*) from gtt_t_kenyon;
+begin;
+cluster gtt_t_kenyon using idx_gtt_t_kenyon_1;
+commit;
+select count(*) from gtt_t_kenyon;
+
+begin;
+cluster gtt_t_kenyon using idx_gtt_t_kenyon_1;
+rollback;
+select count(*) from gtt_t_kenyon;
+
+vacuum full gtt_t_kenyon;
+select count(*) from gtt_t_kenyon;
+
+begin;
+truncate gtt_t_kenyon;
+insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500);
+cluster gtt_t_kenyon using idx_gtt_t_kenyon_1;
+commit;
+select count(*) from gtt_t_kenyon;
+
+insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',2000);
+insert into gtt_t_kenyon select generate_series(3,4),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',4000);
+insert into gtt_t_kenyon select generate_series(5,6),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',5500);
+begin;
+truncate gtt_t_kenyon;
+insert into gtt_t_kenyon select generate_series(1,2000),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God',500);
+cluster gtt_t_kenyon using idx_gtt_t_kenyon_1;
+rollback;
+select count(*) from gtt_t_kenyon;
+
+insert into gtt_with_seq (c1) values(1);
+select * from gtt_with_seq;
+
+reset search_path;
+
diff --git a/src/test/regress/sql/gtt_prepare.sql b/src/test/regress/sql/gtt_prepare.sql
new file mode 100644
index 0000000..dbe84d1
--- /dev/null
+++ b/src/test/regress/sql/gtt_prepare.sql
@@ -0,0 +1,19 @@
+
+CREATE SCHEMA IF NOT EXISTS gtt;
+
+set search_path=gtt,sys;
+
+create global temp table gtt1(a int primary key, b text) on commit delete rows;
+
+create global temp table gtt2(a int primary key, b text) on commit delete rows;
+
+create global temp table gtt3(a int primary key, b text);
+
+create global temp table gtt_t_kenyon(id int,vname varchar(48),remark text) on commit PRESERVE rows;
+create index idx_gtt_t_kenyon_1 on gtt_t_kenyon(id);
+create index idx_gtt_t_kenyon_2 on gtt_t_kenyon(remark);
+
+CREATE GLOBAL TEMPORARY TABLE gtt_with_seq(c1 bigint, c2 bigserial) on commit PRESERVE rows;
+
+reset search_path;
+
diff --git a/src/test/regress/sql/gtt_stats.sql b/src/test/regress/sql/gtt_stats.sql
new file mode 100644
index 0000000..d61b0ff
--- /dev/null
+++ b/src/test/regress/sql/gtt_stats.sql
@@ -0,0 +1,46 @@
+
+CREATE SCHEMA IF NOT EXISTS gtt_stats;
+
+set search_path=gtt_stats,sys;
+
+-- expect 0
+select count(*) from pg_gtt_attached_pids;
+
+-- expect 0
+select count(*) from pg_list_gtt_relfrozenxids();
+
+create global temp table gtt_stats.gtt(a int primary key, b text) on commit PRESERVE rows;
+-- expect 0
+select count(*) from pg_gtt_attached_pids;
+
+-- expect 0
+select count(*) from pg_list_gtt_relfrozenxids();
+
+insert into gtt values(generate_series(1,10000),'test');
+
+-- expect 1
+select count(*) from pg_gtt_attached_pids;
+
+-- expect 2
+select count(*) from pg_list_gtt_relfrozenxids();
+
+-- expect 2
+select schemaname, tablename, relpages, reltuples, relallvisible from pg_gtt_relstats where schemaname = 'gtt_stats' order by tablename;
+
+-- expect 0
+select * from pg_gtt_stats order by tablename;
+
+reindex table gtt;
+
+reindex index gtt_pkey;
+
+analyze gtt;
+
+select schemaname, tablename, relpages, reltuples, relallvisible from pg_gtt_relstats where schemaname = 'gtt_stats' order by tablename;
+
+select * from pg_gtt_stats order by tablename;
+
+reset search_path;
+
+drop schema gtt_stats cascade;
+
