global_temporary_table_v2-pg13.patch

application/octet-stream

Filename: global_temporary_table_v2-pg13.patch
Type: application/octet-stream
Part: 0
Message: Re: [Proposal] Global temporary tables

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v2
Subject: global_temporary_table_v2-pg13
File+
contrib/Makefile 2 1
contrib/pg_gtt/Makefile 22 0
contrib/pg_gtt/pg_gtt--1.0.sql 28 0
contrib/pg_gtt/pg_gtt.c 474 0
contrib/pg_gtt/pg_gtt.control 4 0
src/backend/access/common/reloptions.c 15 0
src/backend/access/gist/gistutil.c 3 1
src/backend/access/hash/hash.c 3 1
src/backend/access/heap/heapam_handler.c 2 2
src/backend/access/heap/vacuumlazy.c 18 4
src/backend/access/nbtree/nbtpage.c 8 1
src/backend/access/transam/xlog.c 4 0
src/backend/catalog/catalog.c 2 0
src/backend/catalog/heap.c 70 16
src/backend/catalog/index.c 75 12
src/backend/catalog/Makefile 2 0
src/backend/catalog/namespace.c 7 0
src/backend/catalog/storage.c 16 1
src/backend/catalog/storage_gtt.c 813 0
src/backend/commands/analyze.c 50 25
src/backend/commands/cluster.c 6 0
src/backend/commands/indexcmds.c 10 0
src/backend/commands/lockcmds.c 2 1
src/backend/commands/tablecmds.c 107 4
src/backend/commands/vacuum.c 94 46
src/backend/optimizer/path/allpaths.c 7 1
src/backend/optimizer/plan/planner.c 2 0
src/backend/optimizer/util/plancat.c 20 13
src/backend/parser/analyze.c 5 0
src/backend/parser/gram.y 4 16
src/backend/parser/parse_relation.c 47 0
src/backend/parser/parse_utilcmd.c 7 0
src/backend/postmaster/autovacuum.c 8 1
src/backend/storage/buffer/bufmgr.c 27 4
src/backend/storage/ipc/ipci.c 4 0
src/backend/storage/ipc/procarray.c 75 0
src/backend/storage/lmgr/proc.c 2 0
src/backend/storage/smgr/md.c 6 0
src/backend/utils/adt/dbsize.c 4 0
src/backend/utils/adt/selfuncs.c 78 15
src/backend/utils/cache/lsyscache.c 14 0
src/backend/utils/cache/relcache.c 18 1
src/backend/utils/misc/guc.c 21 0
src/bin/pg_dump/pg_dump.c 9 2
src/include/catalog/pg_class.h 1 0
src/include/catalog/storage_gtt.h 39 0
src/include/catalog/storage.h 1 1
src/include/parser/parse_relation.h 3 0
src/include/storage/lwlock.h 1 0
src/include/storage/procarray.h 2 0
src/include/storage/proc.h 2 0
src/include/utils/guc.h 4 0
src/include/utils/rel.h 14 1
src/test/regress/expected/gtt_clean.out 7 0
src/test/regress/expected/gtt_error.out 106 0
src/test/regress/expected/gtt_parallel_1.out 84 0
src/test/regress/expected/gtt_parallel_2.out 142 0
src/test/regress/expected/gtt_prepare.out 7 0
src/test/regress/parallel_schedule 6 0
src/test/regress/sql/gtt_clean.sql 6 0
src/test/regress/sql/gtt_error.sql 106 0
src/test/regress/sql/gtt_parallel_1.sql 42 0
src/test/regress/sql/gtt_parallel_2.sql 62 0
src/test/regress/sql/gtt_prepare.sql 15 0
From 979b6f9dda32d0f4735a6a2eb43076c3d822b1d4 Mon Sep 17 00:00:00 2001
From: 义从 <wenjing.zwj@alibaba-inc.com>
Date: Fri, 8 Nov 2019 11:35:02 +0800
Subject: [PATCH] global_temporary_table_v2-pg13

---
 contrib/Makefile                             |   3 ++-
 contrib/pg_gtt/Makefile                      |  22 ++++++++++++++++++++++
 contrib/pg_gtt/pg_gtt--1.0.sql               |  28 ++++++++++++++++++++++++++++
 contrib/pg_gtt/pg_gtt.c                      | 474 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 contrib/pg_gtt/pg_gtt.control                |   4 ++++
 src/backend/access/common/reloptions.c       |  15 +++++++++++++++
 src/backend/access/gist/gistutil.c           |   4 +++-
 src/backend/access/hash/hash.c               |   4 +++-
 src/backend/access/heap/heapam_handler.c     |   4 ++--
 src/backend/access/heap/vacuumlazy.c         |  22 ++++++++++++++++++----
 src/backend/access/nbtree/nbtpage.c          |   9 ++++++++-
 src/backend/access/transam/xlog.c            |   4 ++++
 src/backend/catalog/Makefile                 |   2 ++
 src/backend/catalog/catalog.c                |   2 ++
 src/backend/catalog/heap.c                   |  86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
 src/backend/catalog/index.c                  |  87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
 src/backend/catalog/namespace.c              |   7 +++++++
 src/backend/catalog/storage.c                |  17 ++++++++++++++++-
 src/backend/catalog/storage_gtt.c            | 813 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/backend/commands/analyze.c               |  75 ++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
 src/backend/commands/cluster.c               |   6 ++++++
 src/backend/commands/indexcmds.c             |  10 ++++++++++
 src/backend/commands/lockcmds.c              |   3 ++-
 src/backend/commands/tablecmds.c             | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/backend/commands/vacuum.c                | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------
 src/backend/optimizer/path/allpaths.c        |   8 +++++++-
 src/backend/optimizer/plan/planner.c         |   2 ++
 src/backend/optimizer/util/plancat.c         |  33 ++++++++++++++++++++-------------
 src/backend/parser/analyze.c                 |   5 +++++
 src/backend/parser/gram.y                    |  20 ++++----------------
 src/backend/parser/parse_relation.c          |  47 +++++++++++++++++++++++++++++++++++++++++++++++
 src/backend/parser/parse_utilcmd.c           |   7 +++++++
 src/backend/postmaster/autovacuum.c          |   9 ++++++++-
 src/backend/storage/buffer/bufmgr.c          |  31 +++++++++++++++++++++++++++----
 src/backend/storage/ipc/ipci.c               |   4 ++++
 src/backend/storage/ipc/procarray.c          |  75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/backend/storage/lmgr/proc.c              |   2 ++
 src/backend/storage/smgr/md.c                |   6 ++++++
 src/backend/utils/adt/dbsize.c               |   4 ++++
 src/backend/utils/adt/selfuncs.c             |  93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
 src/backend/utils/cache/lsyscache.c          |  14 ++++++++++++++
 src/backend/utils/cache/relcache.c           |  19 ++++++++++++++++++-
 src/backend/utils/misc/guc.c                 |  21 +++++++++++++++++++++
 src/bin/pg_dump/pg_dump.c                    |  11 +++++++++--
 src/include/catalog/pg_class.h               |   1 +
 src/include/catalog/storage.h                |   2 +-
 src/include/catalog/storage_gtt.h            |  39 +++++++++++++++++++++++++++++++++++++++
 src/include/parser/parse_relation.h          |   3 +++
 src/include/storage/lwlock.h                 |   1 +
 src/include/storage/proc.h                   |   2 ++
 src/include/storage/procarray.h              |   2 ++
 src/include/utils/guc.h                      |   4 ++++
 src/include/utils/rel.h                      |  15 ++++++++++++++-
 src/test/regress/expected/gtt_clean.out      |   7 +++++++
 src/test/regress/expected/gtt_error.out      | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/test/regress/expected/gtt_parallel_1.out |  84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/test/regress/expected/gtt_parallel_2.out | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/test/regress/expected/gtt_prepare.out    |   7 +++++++
 src/test/regress/parallel_schedule           |   6 ++++++
 src/test/regress/sql/gtt_clean.sql           |   6 ++++++
 src/test/regress/sql/gtt_error.sql           | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/test/regress/sql/gtt_parallel_1.sql      |  42 ++++++++++++++++++++++++++++++++++++++++++
 src/test/regress/sql/gtt_parallel_2.sql      |  62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/test/regress/sql/gtt_prepare.sql         |  15 +++++++++++++++
 64 files changed, 2845 insertions(+), 170 deletions(-)
 create mode 100644 contrib/pg_gtt/Makefile
 create mode 100644 contrib/pg_gtt/pg_gtt--1.0.sql
 create mode 100644 contrib/pg_gtt/pg_gtt.c
 create mode 100644 contrib/pg_gtt/pg_gtt.control
 create mode 100644 src/backend/catalog/storage_gtt.c
 create mode 100644 src/include/catalog/storage_gtt.h
 create mode 100644 src/test/regress/expected/gtt_clean.out
 create mode 100644 src/test/regress/expected/gtt_error.out
 create mode 100644 src/test/regress/expected/gtt_parallel_1.out
 create mode 100644 src/test/regress/expected/gtt_parallel_2.out
 create mode 100644 src/test/regress/expected/gtt_prepare.out
 create mode 100644 src/test/regress/sql/gtt_clean.sql
 create mode 100644 src/test/regress/sql/gtt_error.sql
 create mode 100644 src/test/regress/sql/gtt_parallel_1.sql
 create mode 100644 src/test/regress/sql/gtt_parallel_2.sql
 create mode 100644 src/test/regress/sql/gtt_prepare.sql

diff --git a/contrib/Makefile b/contrib/Makefile
index 92184ed..4b1a596 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -48,7 +48,8 @@ SUBDIRS = \
 		tsm_system_rows \
 		tsm_system_time \
 		unaccent	\
-		vacuumlo
+		vacuumlo	\
+		pg_gtt
 
 ifeq ($(with_openssl),yes)
 SUBDIRS += sslinfo
diff --git a/contrib/pg_gtt/Makefile b/contrib/pg_gtt/Makefile
new file mode 100644
index 0000000..1d2ef64
--- /dev/null
+++ b/contrib/pg_gtt/Makefile
@@ -0,0 +1,22 @@
+# contrib/pg_gtt/Makefile
+
+MODULE_big = pg_gtt
+OBJS = pg_gtt.o
+
+EXTENSION = pg_gtt
+DATA = pg_gtt--1.0.sql
+
+LDFLAGS_SL += $(filter -lm, $(LIBS))
+
+NO_INSTALLCHECK = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/pg_pfs
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/pg_gtt/pg_gtt--1.0.sql b/contrib/pg_gtt/pg_gtt--1.0.sql
new file mode 100644
index 0000000..3f794d7
--- /dev/null
+++ b/contrib/pg_gtt/pg_gtt--1.0.sql
@@ -0,0 +1,28 @@
+/* contrib/pg_gtt/pg_gtt--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pg_gtt" to load this file. \quit
+
+
+CREATE FUNCTION pg_gtt_att_statistic(text, text, int)
+RETURNS setof pg_statistic
+AS 'MODULE_PATHNAME','pg_gtt_att_statistic'
+LANGUAGE C STRICT;
+
+CREATE TYPE relstats_type AS (relpages int4, reltuples float4, relallvisible int4, relfrozenxid xid, relminmxid xid);
+CREATE TYPE rel_vac_type AS (pid int4, relfrozenxid xid, relminmxid xid);
+
+CREATE FUNCTION pg_gtt_relstats(text, text)
+RETURNS relstats_type
+AS 'MODULE_PATHNAME','pg_gtt_relstats'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION pg_gtt_attached_pid(text, text)
+RETURNS setof int4
+AS 'MODULE_PATHNAME','pg_gtt_attached_pid'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION pg_list_gtt_relfrozenxids()
+RETURNS setof rel_vac_type
+AS 'MODULE_PATHNAME','pg_list_gtt_relfrozenxids'
+LANGUAGE C STRICT;
diff --git a/contrib/pg_gtt/pg_gtt.c b/contrib/pg_gtt/pg_gtt.c
new file mode 100644
index 0000000..19e1ec0
--- /dev/null
+++ b/contrib/pg_gtt/pg_gtt.c
@@ -0,0 +1,474 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_gtt.c
+ *	code to management function for global temparary table
+ *
+ * IDENTIFICATION
+ *	  contrib/pg_gtt/pg_gtt.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unistd.h>
+#include <sys/time.h>
+#include "port.h"
+#include "access/htup_details.h"
+#include "access/table.h"
+#include "access/xlog.h"
+#include "catalog/pg_type.h"
+#include "catalog/storage_gtt.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_class.h"
+#include "funcapi.h"
+#include "storage/ipc.h"
+#include "storage/shmem.h"
+#include "storage/lwlock.h"
+#include "storage/procarray.h"
+#include "storage/proc.h"
+#include "tcop/utility.h"
+#include "utils/builtins.h"
+#include "utils/memutils.h"
+#include "utils/timeout.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include <nodes/makefuncs.h>
+#include "storage/sinvaladt.h"
+#include "miscadmin.h"
+
+PG_MODULE_MAGIC;
+
+static Oid pg_get_relid(const char *relname, char *schema);
+
+Datum pg_gtt_att_statistic(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(pg_gtt_att_statistic);
+
+Datum pg_gtt_relstats(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(pg_gtt_relstats);
+
+Datum pg_gtt_attached_pid(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(pg_gtt_attached_pid);
+
+Datum pg_list_gtt_relfrozenxids(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(pg_list_gtt_relfrozenxids);
+
+void		_PG_init(void);
+void		_PG_fini(void);
+
+void
+_PG_init(void)
+{
+	return;
+}
+
+void
+_PG_fini(void)
+{
+	return;
+}
+
+static Oid
+pg_get_relid(const char *relname, char *schema)
+{
+	Oid			relid;
+	RangeVar   *rv = makeRangeVar(schema, (char *)relname, -1);
+
+	relid = RangeVarGetRelid(rv, NoLock, true);
+
+	pfree(rv);
+	return relid;
+}
+
+Datum
+pg_gtt_att_statistic(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo	*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc		tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext	oldcontext;
+	HeapTuple		tuple;
+	Relation	rel = NULL;
+	char	*relname = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	char	*schema = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	int		attnum = PG_GETARG_INT32(2);
+	Oid		reloid;
+	char	rel_persistence;
+
+	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")));
+
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = NULL;
+	rsinfo->setDesc = NULL;
+
+	tupdesc = CreateTemplateTupleDesc(31);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starelid",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "staattnum",
+						INT2OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "stainherit",
+						BOOLOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "stanullfrac",
+						FLOAT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "stawidth",
+						INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "stadistinct",
+						FLOAT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "stakind1",
+						INT2OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "stakind2",
+						INT2OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "stakind3",
+						INT2OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "stakind4",
+						INT2OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "stakind5",
+						INT2OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "staop1",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "staop2",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 14, "staop3",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 15, "staop4",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 16, "staop5",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 17, "stacoll1",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 18, "stacoll2",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 19, "stacoll3",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 20, "stacoll4",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 21, "stacoll5",
+						OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 22, "stanumbers1",
+						FLOAT4ARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 23, "stanumbers2",
+						FLOAT4ARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 24, "stanumbers3",
+						FLOAT4ARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 25, "stanumbers4",
+						FLOAT4ARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 26, "stanumbers5",
+						FLOAT4ARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 27, "stavalues1",
+						ANYARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 28, "stavalues2",
+						ANYARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 29, "stavalues3",
+						ANYARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 30, "stavalues4",
+						ANYARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 31, "stavalues5",
+						ANYARRAYOID, -1, 0);
+
+	oldcontext = MemoryContextSwitchTo(
+			rsinfo->econtext->ecxt_per_query_memory);
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
+
+	if (schema == NULL)
+		schema = "public";
+
+	reloid = pg_get_relid(relname, schema);
+	if (reloid == InvalidOid)
+	{
+		elog(WARNING, "relation %s.%s does not exist", schema, relname);
+		return (Datum) 0;
+	}
+
+	rel = table_open(reloid, AccessShareLock);
+	rel_persistence = get_rel_persistence(reloid);
+	if (rel_persistence != RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		elog(WARNING, "relation %s.%s not global temp table", schema, relname);
+		return (Datum) 0;
+	}
+
+	tuple = get_gtt_att_statistic(reloid, attnum, false);
+	if (tuple)
+	{
+		HeapTuple tp = heap_copytuple(tuple);
+		tuplestore_puttuple(tupstore, tp);
+	}
+	tuplestore_donestoring(tupstore);
+
+	table_close(rel, NoLock);
+	return (Datum) 0;
+}
+
+Datum
+pg_gtt_relstats(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo	*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	MemoryContext	oldcontext;
+	HeapTuple	tuple;
+	char	*relname = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	char	*schema = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	Oid	reloid;
+	char	rel_persistence;
+	Datum	values[5];
+	bool	isnull[5];
+	BlockNumber	relpages = 0;
+	double		reltuples = 0;
+	BlockNumber	relallvisible = 0;
+	uint32		relfrozenxid = 0;
+	uint32		relminmxid = 0;
+	Relation	rel = NULL;
+
+	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")));
+
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = NULL;
+	rsinfo->setDesc = NULL;
+
+	tupdesc = CreateTemplateTupleDesc(5);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relpages",
+						INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "reltuples",
+						FLOAT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relallvisible",
+						INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "relfrozenxid",
+						XIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "relminmxid",
+						XIDOID, -1, 0);
+
+	oldcontext = MemoryContextSwitchTo(
+			rsinfo->econtext->ecxt_per_query_memory);
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
+
+	if (schema == NULL)
+		schema = "public";
+
+	reloid = pg_get_relid(relname, schema);
+	if (reloid == InvalidOid)
+	{
+		elog(WARNING, "relation %s.%s does not exist", schema, relname);
+		return (Datum) 0;
+	}
+
+	rel = table_open(reloid, AccessShareLock);
+	rel_persistence = get_rel_persistence(reloid);
+	if (rel_persistence != RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		elog(WARNING, "relation %s.%s not global temp table", schema, relname);
+		table_close(rel, NoLock);
+		return (Datum) 0;
+	}
+
+	get_gtt_relstats(reloid,
+					&relpages, &reltuples, &relallvisible,
+					&relfrozenxid, &relminmxid);
+
+	memset(isnull, false, sizeof(isnull));
+	memset(values, 0, sizeof(values));
+	values[0] = Int32GetDatum(relpages);
+	values[1] = Float4GetDatum((float4)reltuples);
+	values[2] = Int32GetDatum(relallvisible);
+	values[3] = UInt32GetDatum(relfrozenxid);
+	values[4] = UInt32GetDatum(relminmxid);
+	tuple = heap_form_tuple(tupdesc, values, isnull);
+	tuplestore_puttuple(tupstore, tuple);
+	tuplestore_donestoring(tupstore);
+
+	table_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;
+	char	*relname = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	char	*schema = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	Oid	reloid;
+	char	rel_persistence;
+	Datum	values[1];
+	bool	isnull[1];
+	Relation	rel = NULL;
+	PGPROC		*proc = NULL;
+	Bitmapset	*map = NULL;
+	pid_t		pid = 0;
+	int			backendid = 0;
+
+	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")));
+
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = NULL;
+	rsinfo->setDesc = NULL;
+
+	tupdesc = CreateTemplateTupleDesc(1);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pid",
+						INT4OID, -1, 0);
+
+	oldcontext = MemoryContextSwitchTo(
+			rsinfo->econtext->ecxt_per_query_memory);
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
+
+	if (schema == NULL)
+		schema = "public";
+
+	reloid = pg_get_relid(relname, schema);
+	if (reloid == InvalidOid)
+	{
+		elog(WARNING, "relation %s.%s does not exist", schema, relname);
+		return (Datum) 0;
+	}
+
+	rel = table_open(reloid, AccessShareLock);
+	rel_persistence = get_rel_persistence(reloid);
+	if (rel_persistence != RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		elog(WARNING, "relation %s.%s not global temp table", schema, relname);
+		return (Datum) 0;
+	}
+
+	map = copy_active_gtt_bitmap(rel->rd_node);
+	if (map)
+	{
+		backendid = bms_first_member(map);
+
+		do
+		{
+			proc = BackendIdGetProc(backendid);
+			pid = proc->pid;
+			if (pid > 0)
+			{
+				memset(isnull, false, sizeof(isnull));
+				memset(values, 0, sizeof(values));
+				values[0] = 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);
+	table_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;
+	Datum	values[3];
+	bool	isnull[3];
+	int			num_xid = MaxBackends + 1;
+	int			*pids = NULL;
+	uint32		*xids = NULL;
+	int			i = 0;
+	int			j = 0;
+	uint32		oldest = 0;
+
+	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")));
+
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = NULL;
+	rsinfo->setDesc = NULL;
+
+	tupdesc = CreateTemplateTupleDesc(3);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pid",
+						INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "relfrozenxid",
+						XIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relminmxid",
+						XIDOID, -1, 0);
+
+	oldcontext = MemoryContextSwitchTo(
+			rsinfo->econtext->ecxt_per_query_memory);
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	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++)
+		{
+			memset(isnull, false, sizeof(isnull));
+			memset(values, 0, sizeof(values));
+			values[0] = Int32GetDatum(pids[j]);
+			values[1] = UInt32GetDatum(xids[j]);
+			values[2] = UInt32GetDatum(0);
+			tuple = heap_form_tuple(tupdesc, values, isnull);
+			tuplestore_puttuple(tupstore, tuple);
+		}
+	}
+	tuplestore_donestoring(tupstore);
+	pfree(pids);
+	pfree(xids);
+
+	return (Datum) 0;
+}
+
diff --git a/contrib/pg_gtt/pg_gtt.control b/contrib/pg_gtt/pg_gtt.control
new file mode 100644
index 0000000..342af1d
--- /dev/null
+++ b/contrib/pg_gtt/pg_gtt.control
@@ -0,0 +1,4 @@
+comment = 'pg_gtt'
+default_version = '1.0'
+module_pathname = '$libdir/pg_gtt'
+relocatable = true
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index d8790ad..7e30039 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -158,6 +158,19 @@ static relopt_bool boolRelOpts[] =
 		},
 		true
 	},
+	/*
+	 * For global temp table only
+	 * use AccessExclusiveLock for ensure safety
+	 */
+	{
+		{
+			"on_commit_delete_rows",
+			"global temp table on commit options",
+			RELOPT_KIND_HEAP,
+			ShareUpdateExclusiveLock
+		},
+		true
+	},	
 	/* list terminator */
 	{{NULL}}
 };
@@ -1475,6 +1488,8 @@ bytea *
 default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 {
 	static const relopt_parse_elt tab[] = {
+		{"on_commit_delete_rows", RELOPT_TYPE_BOOL,
+		offsetof(StdRdOptions, on_commit_delete_rows)},
 		{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
 		{"autovacuum_enabled", RELOPT_TYPE_BOOL,
 		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index a23dec7..79bdb26 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -1014,7 +1014,9 @@ gistGetFakeLSN(Relation rel)
 {
 	static XLogRecPtr counter = FirstNormalUnloggedLSN;
 
-	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 5cc30da..49b7d2e 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -148,7 +148,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 2dd8821..24c56b9 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -601,7 +601,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
@@ -654,7 +654,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 a3c4a1d..03fac55 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -60,6 +60,7 @@
 #include "utils/pg_rusage.h"
 #include "utils/timestamp.h"
 
+#include "catalog/storage_gtt.h"
 
 /*
  * Space/time tradeoff parameters: do these need to be user-tunable?
@@ -214,8 +215,10 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
 	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));
+	Assert((RELATION_IS_GLOBAL_TEMP(onerel) && onerel->rd_rel->relfrozenxid == InvalidTransactionId) ||
+		(!RELATION_IS_GLOBAL_TEMP(onerel) && TransactionIdIsNormal(onerel->rd_rel->relfrozenxid)));
+	Assert((RELATION_IS_GLOBAL_TEMP(onerel) && onerel->rd_rel->relminmxid == InvalidMultiXactId) ||
+		(!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)
@@ -274,8 +277,19 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
 
 	vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
 
-	vacrelstats->old_rel_pages = onerel->rd_rel->relpages;
-	vacrelstats->old_live_tuples = onerel->rd_rel->reltuples;
+	/* get relstat from gtt localhash */
+	if (RELATION_IS_GLOBAL_TEMP(onerel))
+	{
+		get_gtt_relstats(RelationGetRelid(onerel),
+						&vacrelstats->old_rel_pages,
+						&vacrelstats->old_live_tuples,
+						NULL, NULL, NULL);
+	}
+	else
+	{
+		vacrelstats->old_rel_pages = onerel->rd_rel->relpages;
+		vacrelstats->old_live_tuples = onerel->rd_rel->reltuples;
+	}
 	vacrelstats->num_index_scans = 0;
 	vacrelstats->pages_removed = 0;
 	vacrelstats->lock_waiter_detected = false;
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 268f869..8b2e610 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -763,7 +763,14 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access)
 		/* Read an existing block of the relation */
 		buf = ReadBuffer(rel, blkno);
 		LockBuffer(buf, access);
-		_bt_checkpage(rel, buf);
+
+		/* global temp table may be not yet initialized for this backend. */
+		if (RELATION_IS_GLOBAL_TEMP(rel) &&
+			blkno == BTREE_METAPAGE &&
+			PageIsNew(BufferGetPage(buf)))
+			_bt_initmetapage(BufferGetPage(buf), P_NONE, 0);
+		else
+			_bt_checkpage(rel, buf);
 	}
 	else
 	{
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 2e3cc51..b8a57b6 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6314,6 +6314,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/catalog/Makefile b/src/backend/catalog/Makefile
index a511532..665b7fb 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -44,6 +44,8 @@ OBJS = \
 	storage.o \
 	toasting.o
 
+OBJS += storage_gtt.o
+
 BKIFILES = postgres.bki postgres.description postgres.shdescription
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 1af31c2..ecb516e 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -399,7 +399,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 b7bcdd9..6e089dd 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"
@@ -100,6 +101,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 								Oid reloftype,
 								Oid relowner,
 								char relkind,
+								char relpersistence,
 								TransactionId relfrozenxid,
 								TransactionId relminmxid,
 								Datum relacl,
@@ -405,6 +407,10 @@ heap_create(const char *relname,
 									 relpersistence,
 									 relkind);
 
+	/* global temp table not create storage file when catalog create */
+	if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		create_storage = false;
+
 	/*
 	 * Have the storage manager create the relation's disk file, if needed.
 	 *
@@ -428,7 +434,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:
@@ -925,6 +931,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid reloftype,
 					Oid relowner,
 					char relkind,
+					char relpersistence,
 					TransactionId relfrozenxid,
 					TransactionId relminmxid,
 					Datum relacl,
@@ -963,8 +970,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;
@@ -1326,6 +1343,7 @@ heap_create_with_catalog(const char *relname,
 						reloftypeid,
 						ownerid,
 						relkind,
+						relpersistence,
 						relfrozenxid,
 						relminmxid,
 						PointerGetDatum(relacl),
@@ -1410,11 +1428,15 @@ heap_create_with_catalog(const char *relname,
 	 */
 	StoreConstraints(new_rel_desc, cooked_constraints, is_internal);
 
-	/*
-	 * If there's a special on-commit action, remember it
-	 */
-	if (oncommit != ONCOMMIT_NOOP)
-		register_on_commit_action(relid, oncommit);
+	/* global temp table register action when storage init */
+	if (relpersistence != RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		/*
+		 * If there's a special on-commit action, remember it
+		 */
+		if (oncommit != ONCOMMIT_NOOP)
+			register_on_commit_action(relid, oncommit);
+	}
 
 	/*
 	 * ok, the relation has been cataloged, so close our relations and return
@@ -1906,6 +1928,13 @@ 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(rel->rd_node))
+			elog(ERROR, "can not drop relation when other backend attached this global temp table");
+	}
+
 	/*
 	 * Schedule unlinking of the relation's physical files at commit.
 	 */
@@ -3132,9 +3161,10 @@ RemoveStatistics(Oid relid, AttrNumber attnum)
  *
  * The routine will truncate and then reconstruct the indexes on
  * the specified relation.  Caller must hold exclusive lock on rel.
+ *
  */
 static void
-RelationTruncateIndexes(Relation heapRelation)
+RelationTruncateIndexes(Relation heapRelation, LOCKMODE lockmode)
 {
 	ListCell   *indlist;
 
@@ -3146,7 +3176,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 */
 		indexInfo = BuildIndexInfo(currentIndex);
@@ -3185,8 +3215,13 @@ heap_truncate(List *relids)
 	{
 		Oid			rid = lfirst_oid(cell);
 		Relation	rel;
+		LOCKMODE	lockmode = AccessExclusiveLock;
 
-		rel = table_open(rid, AccessExclusiveLock);
+		/* truncate global temp table only need RowExclusiveLock */
+		if (get_rel_persistence(rid) == RELPERSISTENCE_GLOBAL_TEMP)
+			lockmode = RowExclusiveLock;
+
+		rel = table_open(rid, lockmode);
 		relations = lappend(relations, rel);
 	}
 
@@ -3219,6 +3254,8 @@ void
 heap_truncate_one_rel(Relation rel)
 {
 	Oid			toastrelid;
+	LOCKMODE	lockmode = AccessExclusiveLock;
+	bool		truncate_toastrel = false;
 
 	/*
 	 * Truncate the relation.  Partitioned tables have no storage, so there is
@@ -3227,23 +3264,40 @@ heap_truncate_one_rel(Relation rel)
 	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 		return;
 
+	toastrelid = rel->rd_rel->reltoastrelid;
+
+	/*
+	 * Truncate global temp table only need RowExclusiveLock
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+	{
+		lockmode = RowExclusiveLock;
+		if (OidIsValid(toastrelid) &&
+			gtt_storage_attached(toastrelid))
+			truncate_toastrel = true;
+	}
+	else if (OidIsValid(toastrelid))
+		truncate_toastrel = true;
+
 	/* 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))
+	if (truncate_toastrel)
 	{
-		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 7c34509..a9ed190 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -52,6 +52,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"
@@ -879,6 +880,26 @@ index_create(Relation heapRelation,
 						indexRelationName, RelationGetRelationName(heapRelation))));
 	}
 
+	if (RELATION_IS_GLOBAL_TEMP(heapRelation))
+	{
+		if (accessMethodObjectId != BTREE_AM_OID)
+			elog(ERROR, "only support btree index on global temp table");
+
+		/* We allow to create index on global temp table only this session use it */
+		if (is_other_backend_use_gtt(heapRelation->rd_node))
+			elog(ERROR, "can not create index when have other backend attached this global temp table");
+
+		/* No support create index on global temp table use concurrent mode yet */
+		if (concurrent)
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("cannot create indexes on global temporary tables using concurrent mode")));
+
+		/* if global temp table not init storage, then skip build index */
+		if (!gtt_storage_attached(heapRelation->rd_node.relNode))
+			flags |= INDEX_CREATE_SKIP_BUILD;
+	}
+
 	/*
 	 * construct tuple descriptor for index tuples
 	 */
@@ -2047,6 +2068,13 @@ 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(userHeapRelation->rd_node))
+			elog(ERROR, "can not drop index when other backend attached this global temp table");
+	}
+
 	/*
 	 * Drop Index Concurrently is more or less the reverse process of Create
 	 * Index Concurrently.
@@ -2688,20 +2716,29 @@ index_update_stats(Relation rel,
 		else					/* don't bother for indexes */
 			relallvisible = 0;
 
-		if (rd_rel->relpages != (int32) relpages)
+		/* update index stats into localhash for global temp table */
+		if (RELATION_IS_GLOBAL_TEMP(rel))
 		{
-			rd_rel->relpages = (int32) relpages;
-			dirty = true;
+			up_gtt_relstats(rel, relpages, reltuples, relallvisible,
+							InvalidTransactionId, InvalidMultiXactId);
 		}
-		if (rd_rel->reltuples != (float4) reltuples)
-		{
-			rd_rel->reltuples = (float4) reltuples;
-			dirty = true;
-		}
-		if (rd_rel->relallvisible != (int32) relallvisible)
+		else
 		{
-			rd_rel->relallvisible = (int32) relallvisible;
-			dirty = true;
+			if (rd_rel->relpages != (int32) relpages)
+			{
+				rd_rel->relpages = (int32) relpages;
+				dirty = true;
+			}
+			if (rd_rel->reltuples != (float4) reltuples)
+			{
+				rd_rel->reltuples = (float4) reltuples;
+				dirty = true;
+			}
+			if (rd_rel->relallvisible != (int32) relallvisible)
+			{
+				rd_rel->relallvisible = (int32) relallvisible;
+				dirty = true;
+			}
 		}
 	}
 
@@ -2816,6 +2853,13 @@ index_build(Relation heapRelation,
 		pgstat_progress_update_multi_param(6, index, val);
 	}
 
+	/* POALR */
+	if (RELATION_IS_GLOBAL_TEMP(indexRelation))
+	{
+		if (!gtt_storage_attached(indexRelation->rd_node.relNode))
+			RelationCreateStorage(indexRelation->rd_node, RELPERSISTENCE_GLOBAL_TEMP, indexRelation);
+	}
+
 	/*
 	 * Call the access method's build procedure
 	 */
@@ -3148,12 +3192,22 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
 	/*
 	 * Scan the index and gather up all the TIDs into a tuplesort object.
 	 */
+	memset(&ivinfo, 0, sizeof(IndexVacuumInfo));
 	ivinfo.index = indexRelation;
 	ivinfo.analyze_only = false;
 	ivinfo.report_progress = true;
 	ivinfo.estimated_count = true;
 	ivinfo.message_level = DEBUG2;
-	ivinfo.num_heap_tuples = heapRelation->rd_rel->reltuples;
+
+	/* get relstats abort global temp table from hashtable, not catalog */
+	if (RELATION_IS_GLOBAL_TEMP(heapRelation))
+	{
+		get_gtt_relstats(RelationGetRelid(heapRelation),
+						NULL, &ivinfo.num_heap_tuples, NULL,
+						NULL, NULL);
+	}
+	else
+		ivinfo.num_heap_tuples = heapRelation->rd_rel->reltuples;
 	ivinfo.strategy = NULL;
 
 	/*
@@ -3411,6 +3465,15 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
 				 errmsg("cannot reindex temporary tables of other sessions")));
 
 	/*
+	 * Because global temp table cannot change relfilenode
+	 * no support reindex on global temp table yet.
+	 */
+	if (RELATION_IS_GLOBAL_TEMP(iRel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot reindex global temporary tables")));
+
+	/*
 	 * Also check for active uses of the index in the current transaction; we
 	 * don't want to reindex underneath an open indexscan.
 	 */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index e251f5a..97bf247 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -647,6 +647,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 625af8d..baf7d61 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -28,6 +28,7 @@
 #include "access/xlogutils.h"
 #include "catalog/storage.h"
 #include "catalog/storage_xlog.h"
+#include "catalog/storage_gtt.h"
 #include "storage/freespace.h"
 #include "storage/smgr.h"
 #include "utils/memutils.h"
@@ -74,9 +75,10 @@ static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
  *
  * This function is transactional. The creation is WAL-logged, and if the
  * 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;
@@ -86,6 +88,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;
@@ -118,6 +122,10 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
 	pending->next = pendingDeletes;
 	pendingDeletes = pending;
 
+	/* remember global temp table storage info to localhash */
+	if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP && rel)
+		remember_gtt_storage_info(rnode, rel);
+
 	return srel;
 }
 
@@ -487,8 +495,15 @@ 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]) && 
+				gtt_storage_attached(srels[i]->smgr_rnode.node.relNode))
+				forget_gtt_storage_info(srels[i]->smgr_rnode.node.relNode);
+		}
+
 		pfree(srels);
 	}
 }
diff --git a/src/backend/catalog/storage_gtt.c b/src/backend/catalog/storage_gtt.c
new file mode 100644
index 0000000..0749afc
--- /dev/null
+++ b/src/backend/catalog/storage_gtt.c
@@ -0,0 +1,813 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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/visibilitymap.h"
+#include "access/xact.h"
+#include "access/xlog.h"
+#include "access/xloginsert.h"
+#include "access/xlogutils.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "catalog/storage.h"
+#include "catalog/storage_xlog.h"
+#include "catalog/storage_gtt.h"
+#include "catalog/heap.h"
+#include "commands/tablecmds.h"
+#include "nodes/primnodes.h"
+#include "nodes/pg_list.h"
+#include "miscadmin.h"
+#include "storage/freespace.h"
+#include "storage/smgr.h"
+#include "storage/ipc.h"
+#include "storage/proc.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/hsearch.h"
+#include "utils/catcache.h"
+#include <utils/relcache.h>
+#include "utils/inval.h"
+#include "utils/guc.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;
+
+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
+{
+	RelFileNode	rnode;
+	Bitmapset	*map;
+	/* bitmap data */
+} gtt_shared_hash_entry;
+
+typedef struct
+{
+	Oid			relid;
+
+	Oid			spcnode;
+	/* pg_class stat */
+	int32		relpages;
+	float4		reltuples;
+	int32		relallvisible;
+	TransactionId relfrozenxid;
+	TransactionId relminmxid;
+	char		relkind;
+	bool		on_commit_delete;
+
+	/* pg_statistic */
+	int			natts;
+	int			*attnum;
+	HeapTuple	*att_stat_tups;
+} gtt_local_hash_entry;
+
+static Size action_gtt_shared_hash_entry_size(void);
+static void gtt_storage_checkin(RelFileNode rnode);
+static void gtt_storage_checkout(RelFileNode rnode, bool skiplock);
+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 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();
+	}
+
+	info.keysize = sizeof(RelFileNode);
+	info.entrysize = gtt_shared_ctl->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(RelFileNode rnode)
+{
+	gtt_shared_hash_entry	*entry;
+	bool			found;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	LWLockAcquire(&gtt_shared_ctl->lock, LW_EXCLUSIVE);
+	entry = (gtt_shared_hash_entry *) hash_search(active_gtt_shared_hash,
+												&rnode, 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_gtt.")));
+	}
+
+	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(RelFileNode rnode, bool skiplock)
+{
+	gtt_shared_hash_entry	*entry;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	if (!skiplock)
+		LWLockAcquire(&gtt_shared_ctl->lock, LW_EXCLUSIVE);
+
+	entry = hash_search(active_gtt_shared_hash,
+					(void *) &(rnode), HASH_FIND, NULL);
+
+	if (entry == NULL)
+	{
+		if (!skiplock)
+			LWLockRelease(&gtt_shared_ctl->lock);
+
+		elog(WARNING, "relfilenode %u/%u/%u not exist in gtt shared hash when forget",
+						rnode.dbNode, rnode.spcNode, rnode.relNode);
+		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, &rnode, HASH_REMOVE, NULL))
+			elog(PANIC, "gtt shared hash table corrupted");
+	}
+
+	if (!skiplock)
+		LWLockRelease(&gtt_shared_ctl->lock);
+
+	return;
+}
+
+Bitmapset *
+copy_active_gtt_bitmap(RelFileNode node)
+{
+	gtt_shared_hash_entry	*entry;
+	Bitmapset	*map_copy = NULL;
+
+	if (max_active_gtt <= 0)
+		return NULL;
+
+	LWLockAcquire(&gtt_shared_ctl->lock, LW_SHARED);
+	entry = hash_search(active_gtt_shared_hash,
+					(void *) &(node), 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(RelFileNode node)
+{
+	gtt_shared_hash_entry	*entry;
+	bool		in_use = false;
+	int			num_use = 0;
+
+	if (max_active_gtt <= 0)
+		return false;
+
+	LWLockAcquire(&gtt_shared_ctl->lock, LW_SHARED);
+	entry = hash_search(active_gtt_shared_hash,
+					(void *) &(node), 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;
+	Oid			relid = rnode.relNode;
+	bool		found;
+
+	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_gtt to enable this feature.")));
+
+	if (RecoveryInProgress())
+		elog(ERROR, "readonly mode not support access global temp table yet");
+
+	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 temp relation table",
+						GTT_LOCAL_HASH_SIZE,
+						&ctl, HASH_ELEM | HASH_BLOBS);
+
+		if (!CacheMemoryContext)
+			CreateCacheMemoryContext();
+
+		gtt_relstats_context =
+			AllocSetContextCreate(CacheMemoryContext,
+							"gtt relstats context",
+							ALLOCSET_DEFAULT_SIZES);
+	}
+
+	/* Look up or create an entry */
+	entry = hash_search(gtt_storage_local_hash,
+					(void *) &relid, HASH_ENTER, &found);
+
+	if (found)
+	{
+		elog(ERROR, "backend %d relid %u already exists in global temp table local hash",
+					MyBackendId, relid);
+	}
+
+	oldcontext = MemoryContextSwitchTo(gtt_relstats_context);
+
+	entry->spcnode = rnode.spcNode;
+	entry->relpages = 0;
+	entry->reltuples = 0;
+	entry->relallvisible = 0;
+	entry->relkind = rel->rd_rel->relkind;
+	/* only heap contain transaction information */
+	if (entry->relkind == RELKIND_RELATION)
+	{
+		int natts = RelationGetNumberOfAttributes(rel);
+		entry->natts = natts;
+		entry->attnum = palloc0(sizeof(int) * natts);
+		entry->att_stat_tups = palloc0(sizeof(HeapTuple) * natts);
+
+		if (RELATION_GTT_ON_COMMIT_DELETE(rel))
+		{
+			entry->on_commit_delete = true;
+			register_on_commit_action(rel->rd_node.relNode, ONCOMMIT_DELETE_ROWS);
+		}
+
+		entry->relfrozenxid = RecentXmin;
+		entry->relminmxid = GetOldestMultiXactId();
+		insert_gtt_relfrozenxid_to_ordered_list(entry->relfrozenxid);
+		set_gtt_session_relfrozenxid();
+		gtt_storage_checkin(rnode);
+	}
+	else
+	{
+		entry->natts = 0;
+		entry->attnum = 0;
+		entry->att_stat_tups = NULL;
+		entry->on_commit_delete = false;
+		entry->relfrozenxid = 0;
+		entry->relminmxid = 0;
+	}
+
+	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)
+{
+	gtt_local_hash_entry		*entry;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	if (gtt_storage_local_hash == NULL)
+		return;
+
+	entry = hash_search(gtt_storage_local_hash,
+				(void *) &(relid), HASH_FIND, NULL);
+
+	if (entry && entry->relkind == RELKIND_RELATION)
+	{
+		RelFileNode rnode;
+
+		rnode.spcNode = entry->spcnode;
+		rnode.dbNode = MyDatabaseId;
+		rnode.relNode = entry->relid;
+
+		gtt_storage_checkout(rnode, false);
+
+		Assert(TransactionIdIsNormal(entry->relfrozenxid));
+		remove_gtt_relfrozenxid_from_ordered_list(entry->relfrozenxid);
+		set_gtt_session_relfrozenxid();
+	}
+
+	hash_search(gtt_storage_local_hash,
+			(void *) &(relid), HASH_REMOVE, NULL);
+
+	return;
+}
+
+/* is the storage file was created in this backend */
+bool
+gtt_storage_attached(Oid relid)
+{
+	bool found;
+
+	if (max_active_gtt <= 0)
+		return false;
+
+	if (gtt_storage_local_hash == NULL)
+		return false;
+
+	hash_search(gtt_storage_local_hash,
+			(void *) &(relid), HASH_FIND, &found);
+
+	return found;
+}
+
+static void
+gtt_storage_removeall(int code, Datum arg)
+{
+	HASH_SEQ_STATUS				status;
+	gtt_local_hash_entry		*entry;
+	int			nrels = 0,
+				maxrels = 0;
+	SMgrRelation	*srels = NULL;
+	RelFileNode		*rnodes = NULL;
+	char			*relkinds = NULL;
+
+	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)
+	{
+		SMgrRelation srel;
+		RelFileNode rnode;
+
+		rnode.spcNode = entry->spcnode;
+		rnode.dbNode = MyDatabaseId;
+		rnode.relNode = entry->relid;
+
+		srel = smgropen(rnode, MyBackendId);
+
+		/* allocate the initial array, or extend it, if needed */
+		if (maxrels == 0)
+		{
+			maxrels = 32;
+			srels = palloc(sizeof(SMgrRelation) * maxrels);
+			rnodes = palloc(sizeof(RelFileNode) * maxrels);
+			relkinds = palloc(sizeof(char) * maxrels);
+		}
+		else if (maxrels <= nrels)
+		{
+			maxrels *= 2;
+			srels = repalloc(srels, sizeof(SMgrRelation) * maxrels);
+			rnodes = repalloc(rnodes, sizeof(RelFileNode) * maxrels);
+			relkinds = repalloc(relkinds, sizeof(char) * maxrels);
+		}
+
+		srels[nrels] = srel;
+		rnodes[nrels] = rnode;
+		relkinds[nrels] = entry->relkind;
+		nrels++;
+	}
+
+	if (nrels > 0)
+	{
+		int i;
+
+		smgrdounlinkall(srels, nrels, false);
+
+		LWLockAcquire(&gtt_shared_ctl->lock, LW_EXCLUSIVE);
+		for (i = 0; i < nrels; i++)
+		{
+			smgrclose(srels[i]);
+			if (relkinds[i] == RELKIND_RELATION)
+				gtt_storage_checkout(rnodes[i], true);
+		}
+		LWLockRelease(&gtt_shared_ctl->lock);
+
+		pfree(srels);
+		pfree(rnodes);
+		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;
+	bool					found;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	if (gtt_storage_local_hash == NULL)
+		return;
+
+	entry = hash_search(gtt_storage_local_hash,
+					(void *) &relid, HASH_FIND, &found);
+
+	if (!found)
+		return;
+
+	Assert(entry->spcnode);
+
+	if (num_pages >= 0 &&
+		entry->relpages != (int32)num_pages)
+		entry->relpages = (int32)num_pages;
+
+	if (num_tuples >= 0 &&
+		num_tuples != (float4)entry->reltuples)
+		entry->reltuples = (float4)num_tuples;
+
+	/* only heap contain transaction information and relallvisible */
+	if (entry->relkind == RELKIND_RELATION)
+	{
+		if (entry->relallvisible >= 0 &&
+			entry->relallvisible != (int32)num_all_visible_pages)
+		{
+			entry->relallvisible = (int32)num_all_visible_pages;
+		}
+
+		if (TransactionIdIsNormal(relfrozenxid) &&
+			entry->relfrozenxid != relfrozenxid &&
+			(TransactionIdPrecedes(entry->relfrozenxid, relfrozenxid) ||
+			 TransactionIdPrecedes(ReadNewTransactionId(), entry->relfrozenxid)))
+		{
+			remove_gtt_relfrozenxid_from_ordered_list(entry->relfrozenxid);
+			entry->relfrozenxid = relfrozenxid;
+			insert_gtt_relfrozenxid_to_ordered_list(relfrozenxid);
+			set_gtt_session_relfrozenxid();
+		}
+
+		if (MultiXactIdIsValid(relminmxid) &&
+			entry->relminmxid != relminmxid &&
+			(MultiXactIdPrecedes(entry->relminmxid, relminmxid) ||
+			 MultiXactIdPrecedes(ReadNextMultiXactId(), entry->relminmxid)))
+		{
+			entry->relminmxid = relminmxid;
+		}
+	}
+
+	return;
+}
+
+/*
+ * Search global temp table relstats(relpage/reltuple/relallvisible) 
+ * from local hashtable.
+ */
+void
+get_gtt_relstats(Oid relid, BlockNumber *relpages, double *reltuples,
+				BlockNumber *relallvisible, TransactionId *relfrozenxid,
+				TransactionId *relminmxid)
+{
+	gtt_local_hash_entry	*entry;
+	bool					found;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	if (gtt_storage_local_hash == NULL)
+		return;
+
+	entry = hash_search(gtt_storage_local_hash,
+					(void *) &relid, HASH_FIND, &found);
+
+	if (!found)
+		return;
+
+	Assert(entry->relid == relid);
+
+	if (relpages)
+		*relpages = entry->relpages;
+
+	if (reltuples)
+		*reltuples = entry->reltuples;
+
+	if (relallvisible)
+		*relallvisible = entry->relallvisible;
+
+	if (relfrozenxid)
+		*relfrozenxid = entry->relfrozenxid;
+
+	if (relminmxid)
+		*relminmxid = entry->relminmxid;
+
+	return;
+}
+
+/*
+ * 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;
+	bool					found;
+	MemoryContext			oldcontext;
+	int						i = 0;
+
+	if (max_active_gtt <= 0)
+		return;
+
+	if (gtt_storage_local_hash == NULL)
+		return;
+
+	entry = hash_search(gtt_storage_local_hash,
+						(void *) &reloid, HASH_FIND, &found);
+
+	if (!found)
+		return;
+
+	if (entry->relkind != RELKIND_RELATION)
+	{
+		elog(WARNING, "oid %u not a relation", reloid);
+		return;
+	}
+
+	/* todo */
+	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;
+	bool					found;
+	int						i = 0;
+
+	if (max_active_gtt <= 0)
+		return NULL;
+
+	if (gtt_storage_local_hash == NULL)
+		return NULL;
+
+	entry = hash_search(gtt_storage_local_hash,
+						(void *) &reloid, HASH_FIND, &found);
+
+	if (!found)
+		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;
+}
+
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 7accb95..e0dc376 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -65,6 +65,7 @@
 #include "utils/syscache.h"
 #include "utils/timestamp.h"
 
+#include "catalog/storage_gtt.h"
 
 /* Per-index data for ANALYZE */
 typedef struct AnlIndexData
@@ -102,7 +103,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);
 
@@ -575,14 +576,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]));
 		}
 
 		/*
@@ -659,11 +661,20 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
 			IndexBulkDeleteResult *stats;
 			IndexVacuumInfo ivinfo;
 
+			memset(&ivinfo, 0, sizeof(IndexVacuumInfo));
 			ivinfo.index = Irel[ind];
 			ivinfo.analyze_only = true;
 			ivinfo.estimated_count = true;
 			ivinfo.message_level = elevel;
-			ivinfo.num_heap_tuples = onerel->rd_rel->reltuples;
+			/* get global temp relstats from localhash, not catalog */
+			if (RELATION_IS_GLOBAL_TEMP(onerel))
+			{
+				get_gtt_relstats(RelationGetRelid(onerel),
+								NULL, &ivinfo.num_heap_tuples, NULL,
+								NULL, NULL);
+			}
+			else
+				ivinfo.num_heap_tuples = onerel->rd_rel->reltuples;
 			ivinfo.strategy = vac_strategy;
 
 			stats = index_vacuum_cleanup(&ivinfo, NULL);
@@ -1425,7 +1436,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;
@@ -1527,31 +1538,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));
+
+			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);
+			heap_freetuple(stup);
+		}
 	}
 
 	table_close(sd, RowExclusiveLock);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index a23128d..0d38988 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -392,6 +392,12 @@ cluster_rel(Oid tableOid, Oid indexOid, int options)
 					 errmsg("cannot vacuum temporary tables of other sessions")));
 	}
 
+	/* not support cluster global temp table yet */
+	if (RELATION_IS_GLOBAL_TEMP(OldHeap))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("not support cluster global temporary tables yet")));
+
 	/*
 	 * Also check for active uses of the relation in the current transaction,
 	 * including open scans and pending AFTER trigger events.
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 374e2d0..8e88a59 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -2585,6 +2585,16 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
 			!isTempNamespace(classtuple->relnamespace))
 			continue;
 
+		/* not support reindex on global temp table, so skip it */
+		if (classtuple->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+		{
+			ereport(WARNING,
+				(errmsg("global temp table \"%s.%s\" skip reindexed",
+					get_namespace_name(get_rel_namespace(relid)),
+					get_rel_name(relid))));
+			continue;
+		}
+
 		/* Check user/system classification, and optionally skip */
 		if (objectKind == REINDEX_OBJECT_SYSTEM &&
 			!IsSystemClass(relid, classtuple))
diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c
index bae3b38..ff9d5cb 100644
--- a/src/backend/commands/lockcmds.c
+++ b/src/backend/commands/lockcmds.c
@@ -107,7 +107,8 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
 	 * transaction.
 	 */
 	relpersistence = get_rel_persistence(relid);
-	if (relpersistence == RELPERSISTENCE_TEMP)
+	if (relpersistence == RELPERSISTENCE_TEMP ||
+		relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
 	/* Check permissions. */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 5597be6..4bf4b9a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -48,6 +48,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"
@@ -531,6 +532,7 @@ static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
 								  Relation partitionTbl);
 static List *GetParentedForeignKeyRefs(Relation partition);
 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
+static bool has_oncommit_option(List *options);
 
 
 /* ----------------------------------------------------------------
@@ -576,6 +578,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	LOCKMODE	parentLockmode;
 	const char *accessMethod = NULL;
 	Oid			accessMethodId = InvalidOid;
+	bool		has_oncommit_clause = false;
 
 	/*
 	 * Truncate relname to appropriate length (probably a waste of time, as
@@ -586,8 +589,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")));
@@ -617,12 +622,28 @@ 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),
 				 errmsg("cannot create temporary table within security-restricted operation")));
 
+	/* Not support partitioned or inherited global temp table yet */
+	if (stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		if (relkind == RELKIND_PARTITIONED_TABLE)
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("not support create global temporary partition table yet")));
+
+		if (stmt->inhRelations)
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("not support create global temporary inheritance table yet")));
+	}
+
 	/*
 	 * Determine the lockmode to use when scanning parents.  A self-exclusive
 	 * lock is needed here.
@@ -718,6 +739,40 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 	/*
 	 * Parse and validate reloptions, if any.
 	 */
+	/* global temp table */
+	has_oncommit_clause = has_oncommit_option(stmt->options);
+	if (stmt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		if (has_oncommit_clause)
+		{
+			if (stmt->oncommit != ONCOMMIT_NOOP)
+				elog(ERROR, "can not defeine global temp table with on commit and with clause at same time");
+		}
+		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("true");
+			else
+				elog(ERROR, "global temp table not support on commit drop clause");
+
+			stmt->options = lappend(stmt->options, opt);
+		}
+	}
+	else if (has_oncommit_clause)
+		elog(ERROR, "regular table cannot specifie on_commit_delete_rows");
+
 	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
 									 true, false);
 
@@ -1772,7 +1827,8 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
 		 * table or the current physical file to be thrown away anyway.
 		 */
 		if (rel->rd_createSubid == mySubid ||
-			rel->rd_newRelfilenodeSubid == mySubid)
+			rel->rd_newRelfilenodeSubid == mySubid ||
+			RELATION_IS_GLOBAL_TEMP(rel))
 		{
 			/* Immediate, non-rollbackable truncation is OK */
 			heap_truncate_one_rel(rel);
@@ -3330,6 +3386,13 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bo
 	 * specially.
 	 */
 	targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
+
+	/* not support rename global temp table yet */
+	if (RELATION_IS_GLOBAL_TEMP(targetrelation))
+		ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			errmsg("not support rename global temporary tables yet")));
+
 	namespaceId = RelationGetNamespace(targetrelation);
 
 	/*
@@ -3499,6 +3562,14 @@ AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt)
 	/* Caller is required to provide an adequate lock. */
 	rel = relation_open(relid, NoLock);
 
+	/* not support alter global temp table yet */
+	if (RELATION_IS_GLOBAL_TEMP(rel))
+	{
+		ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			errmsg("not support alter global temporary tables yet")));
+	}
+
 	CheckTableNotInUse(rel, "ALTER TABLE");
 
 	ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode);
@@ -7679,6 +7750,13 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 				 errmsg("referenced relation \"%s\" is not a table",
 						RelationGetRelationName(pkrel))));
 
+	/* global temp table not support foreign key constraint yet */
+	if (RELATION_IS_GLOBAL_TEMP(pkrel))
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("referenced relation \"%s\" is not a global temp table",
+						RelationGetRelationName(pkrel))));
+
 	if (!allowSystemTableMods && IsSystemRelation(pkrel))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -7718,6 +7796,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;
+		/* global temp table not support foreign key constraint yet */
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+					 errmsg("not support foreign key constraints on global temp table yet")));
+			break;
 	}
 
 	/*
@@ -12726,7 +12810,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,
@@ -14133,7 +14217,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",
@@ -16723,3 +16809,20 @@ ATDetachCheckNoForeignKeyRefs(Relation partition)
 		table_close(rel, NoLock);
 	}
 }
+
+static bool
+has_oncommit_option(List *options)
+{
+	ListCell   *listptr;
+
+	foreach(listptr, options)
+	{
+		DefElem    *def = (DefElem *) lfirst(listptr);
+
+		if (pg_strcasecmp(def->defname, "on_commit_delete_rows") == 0)
+			return true;
+	}
+
+	return false;
+}
+
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index da1da23..70278a6 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"
@@ -1070,12 +1071,25 @@ vac_estimate_reltuples(Relation relation,
 					   BlockNumber scanned_pages,
 					   double scanned_tuples)
 {
-	BlockNumber old_rel_pages = relation->rd_rel->relpages;
-	double		old_rel_tuples = relation->rd_rel->reltuples;
+	BlockNumber old_rel_pages = 0;
+	double		old_rel_tuples = 0;
 	double		old_density;
 	double		unscanned_pages;
 	double		total_tuples;
 
+	/* get relstat from gtt local hash */
+	if (RELATION_IS_GLOBAL_TEMP(relation))
+	{
+		get_gtt_relstats(RelationGetRelid(relation),
+						&old_rel_pages, &old_rel_tuples, NULL,
+						NULL, NULL);
+	}
+	else
+	{
+		old_rel_pages = relation->rd_rel->relpages;
+		old_rel_tuples = relation->rd_rel->reltuples;
+	}
+
 	/* If we did scan the whole table, just use the count as-is */
 	if (scanned_pages >= total_pages)
 		return scanned_tuples;
@@ -1175,24 +1189,8 @@ vac_update_relstats(Relation relation,
 	/* Apply statistical updates, if any, to copied tuple */
 
 	dirty = false;
-	if (pgcform->relpages != (int32) num_pages)
-	{
-		pgcform->relpages = (int32) num_pages;
-		dirty = true;
-	}
-	if (pgcform->reltuples != (float4) num_tuples)
-	{
-		pgcform->reltuples = (float4) num_tuples;
-		dirty = true;
-	}
-	if (pgcform->relallvisible != (int32) num_all_visible_pages)
-	{
-		pgcform->relallvisible = (int32) num_all_visible_pages;
-		dirty = true;
-	}
 
 	/* Apply DDL updates, but not inside an outer transaction (see above) */
-
 	if (!in_outer_xact)
 	{
 		/*
@@ -1217,37 +1215,64 @@ vac_update_relstats(Relation relation,
 		}
 	}
 
-	/*
-	 * Update relfrozenxid, unless caller passed InvalidTransactionId
-	 * indicating it has no new data.
-	 *
-	 * Ordinarily, we don't let relfrozenxid go backwards: if things are
-	 * working correctly, the only way the new frozenxid could be older would
-	 * be if a previous VACUUM was done with a tighter freeze_min_age, in
-	 * which case we don't want to forget the work it already did.  However,
-	 * if the stored relfrozenxid is "in the future", then it must be corrupt
-	 * and it seems best to overwrite it with the cutoff we used this time.
-	 * This should match vac_update_datfrozenxid() concerning what we consider
-	 * to be "in the future".
-	 */
-	if (TransactionIdIsNormal(frozenxid) &&
-		pgcform->relfrozenxid != frozenxid &&
-		(TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid) ||
-		 TransactionIdPrecedes(ReadNewTransactionId(),
-							   pgcform->relfrozenxid)))
+	/* global temp table remember relstats to localhash not catalog */
+	if (RELATION_IS_GLOBAL_TEMP(relation))
 	{
-		pgcform->relfrozenxid = frozenxid;
-		dirty = true;
+		up_gtt_relstats(relation,
+						num_pages, num_tuples,
+						num_all_visible_pages,
+						frozenxid, minmulti);
 	}
-
-	/* Similarly for relminmxid */
-	if (MultiXactIdIsValid(minmulti) &&
-		pgcform->relminmxid != minmulti &&
-		(MultiXactIdPrecedes(pgcform->relminmxid, minmulti) ||
-		 MultiXactIdPrecedes(ReadNextMultiXactId(), pgcform->relminmxid)))
+	else
 	{
-		pgcform->relminmxid = minmulti;
-		dirty = true;
+		if (pgcform->relpages != (int32) num_pages)
+		{
+			pgcform->relpages = (int32) num_pages;
+			dirty = true;
+		}
+		if (pgcform->reltuples != (float4) num_tuples)
+		{
+			pgcform->reltuples = (float4) num_tuples;
+			dirty = true;
+		}
+		if (pgcform->relallvisible != (int32) num_all_visible_pages)
+		{
+			pgcform->relallvisible = (int32) num_all_visible_pages;
+			dirty = true;
+		}
+
+		/*
+		 * Update relfrozenxid, unless caller passed InvalidTransactionId
+		 * indicating it has no new data.
+		 *
+		 * Ordinarily, we don't let relfrozenxid go backwards: if things are
+		 * working correctly, the only way the new frozenxid could be older would
+		 * be if a previous VACUUM was done with a tighter freeze_min_age, in
+		 * which case we don't want to forget the work it already did.  However,
+		 * if the stored relfrozenxid is "in the future", then it must be corrupt
+		 * and it seems best to overwrite it with the cutoff we used this time.
+		 * This should match vac_update_datfrozenxid() concerning what we consider
+		 * to be "in the future".
+		 */
+		if (TransactionIdIsNormal(frozenxid) &&
+			pgcform->relfrozenxid != frozenxid &&
+			(TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid) ||
+			 TransactionIdPrecedes(ReadNewTransactionId(),
+								   pgcform->relfrozenxid)))
+		{
+			pgcform->relfrozenxid = frozenxid;
+			dirty = true;
+		}
+
+		/* Similarly for relminmxid */
+		if (MultiXactIdIsValid(minmulti) &&
+			pgcform->relminmxid != minmulti &&
+			(MultiXactIdPrecedes(pgcform->relminmxid, minmulti) ||
+			 MultiXactIdPrecedes(ReadNextMultiXactId(), pgcform->relminmxid)))
+		{
+			pgcform->relminmxid = minmulti;
+			dirty = true;
+		}
 	}
 
 	/* If anything changed, write out the tuple. */
@@ -1339,6 +1364,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
@@ -1396,6 +1425,25 @@ 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 oldest_gtt_frozenxid =
+			list_all_session_gtt_frozenxids(0, NULL, NULL, NULL);
+
+		if (TransactionIdIsNormal(oldest_gtt_frozenxid) &&
+			TransactionIdPrecedes(oldest_gtt_frozenxid, newFrozenXid))
+		{
+			ereport(WARNING,
+				(errmsg("global temp table oldest FrozenXid is far in the past"),
+				 errhint("please truncate them or kill those sessions that use them.")));
+			newFrozenXid = oldest_gtt_frozenxid;
+		}
+	}
+
 	/* Now fetch the pg_database tuple we need to update. */
 	relation = table_open(DatabaseRelationId, RowExclusiveLock);
 
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index db3a68a..b41a920 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 17c5f08..fa6aee5 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6307,7 +6307,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 e5f9e04..ecc4543 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -53,6 +53,7 @@
 #include "utils/syscache.h"
 #include "utils/snapmgr.h"
 
+#include "catalog/storage_gtt.h"
 
 /* GUC parameter */
 int			constraint_exclusion = CONSTRAINT_EXCLUSION_PARTITION;
@@ -947,10 +948,10 @@ void
 estimate_rel_size(Relation rel, int32 *attr_widths,
 				  BlockNumber *pages, double *tuples, double *allvisfrac)
 {
-	BlockNumber curpages;
-	BlockNumber relpages;
-	double		reltuples;
-	BlockNumber relallvisible;
+	BlockNumber curpages = 0;
+	BlockNumber relpages = 0;
+	double		reltuples = 0;
+	BlockNumber relallvisible = 0;
 	double		density;
 
 	switch (rel->rd_rel->relkind)
@@ -964,6 +965,21 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
 
 		case RELKIND_INDEX:
 
+			/* global temp table get relstats from localhash */
+			if (RELATION_IS_GLOBAL_TEMP(rel))
+			{
+				get_gtt_relstats(RelationGetRelid(rel),
+								&relpages, &reltuples, &relallvisible,
+								NULL, NULL);
+			}
+			else
+			{
+				/* coerce values in pg_class to more desirable types */
+				relpages = (BlockNumber) rel->rd_rel->relpages;
+				reltuples = (double) rel->rd_rel->reltuples;
+				relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
+			}
+
 			/*
 			 * XXX: It'd probably be good to move this into a callback,
 			 * individual index types e.g. know if they have a metapage.
@@ -972,11 +988,6 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
 			/* it has storage, ok to call the smgr */
 			curpages = RelationGetNumberOfBlocks(rel);
 
-			/* coerce values in pg_class to more desirable types */
-			relpages = (BlockNumber) rel->rd_rel->relpages;
-			reltuples = (double) rel->rd_rel->reltuples;
-			relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
-
 			/* report estimated # pages */
 			*pages = curpages;
 			/* quick exit if rel is clearly empty */
@@ -986,10 +997,6 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
 				*allvisfrac = 0;
 				break;
 			}
-			/* coerce values in pg_class to more desirable types */
-			relpages = (BlockNumber) rel->rd_rel->relpages;
-			reltuples = (double) rel->rd_rel->reltuples;
-			relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
 
 			/*
 			 * Discount the metapage while estimating the number of tuples.
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 85d7a96..67b200d 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -2579,6 +2579,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 3f67aaf..333e54f 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3268,17 +3268,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; }
@@ -11505,19 +11499,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 4dd8150..637a15c 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -59,6 +59,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);
 
 
 /*
@@ -3420,3 +3421,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 ee47547..d13e253 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -359,6 +359,13 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
 	AlterSeqStmt *altseqstmt;
 	List	   *attnamelist;
 
+	if (cxt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
+	{
+		ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("Global temp table does not yet support serial column")));
+	}
+
 	/*
 	 * Determine namespace and name to use for the sequence.
 	 *
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index c1dd816..f2d3df9 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2091,6 +2091,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);
@@ -2157,7 +2162,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 7ad1073..8e81701 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -53,6 +53,8 @@
 #include "utils/resowner_private.h"
 #include "utils/timestamp.h"
 
+#include "utils/guc.h"
+#include "catalog/storage_gtt.h"
 
 /* Note: these two macros only work on shared buffers, not local ones! */
 #define BufHdrGetBlock(bufHdr)	((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * BLCKSZ))
@@ -432,7 +434,7 @@ ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref)
 static Buffer ReadBuffer_common(SMgrRelation reln, char relpersistence,
 								ForkNumber forkNum, BlockNumber blockNum,
 								ReadBufferMode mode, BufferAccessStrategy strategy,
-								bool *hit);
+								bool *hit, Relation rel);
 static bool PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy);
 static void PinBuffer_Locked(BufferDesc *buf);
 static void UnpinBuffer(BufferDesc *buf, bool fixOwner);
@@ -664,7 +666,8 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
 	 */
 	pgstat_count_buffer_read(reln);
 	buf = ReadBuffer_common(reln->rd_smgr, reln->rd_rel->relpersistence,
-							forkNum, blockNum, mode, strategy, &hit);
+							forkNum, blockNum, mode, strategy, &hit,
+							reln);
 	if (hit)
 		pgstat_count_buffer_hit(reln);
 	return buf;
@@ -692,7 +695,7 @@ ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
 	Assert(InRecovery);
 
 	return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum,
-							 mode, strategy, &hit);
+							 mode, strategy, &hit, NULL);
 }
 
 
@@ -704,7 +707,8 @@ ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
 static Buffer
 ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 				  BlockNumber blockNum, ReadBufferMode mode,
-				  BufferAccessStrategy strategy, bool *hit)
+				  BufferAccessStrategy strategy, bool *hit,
+				  Relation rel)
 {
 	BufferDesc *bufHdr;
 	Block		bufBlock;
@@ -719,6 +723,15 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 
 	isExtend = (blockNum == P_NEW);
 
+	/* create storage when first read data page for gtt */
+	if (relpersistence == RELPERSISTENCE_GLOBAL_TEMP &&
+		(isExtend || blockNum == 0) &&
+		forkNum == MAIN_FORKNUM &&
+		!gtt_storage_attached(smgr->smgr_rnode.node.relNode))
+	{
+		RelationCreateStorage(smgr->smgr_rnode.node, relpersistence, rel);
+	}
+
 	TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum,
 									   smgr->smgr_rnode.node.spcNode,
 									   smgr->smgr_rnode.node.dbNode,
@@ -2799,6 +2812,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(relation->rd_node.relNode))
+	{
+		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 8853706..b3544dd 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 3da5307..21bd1f2 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -60,6 +60,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))))
 
@@ -3973,3 +3974,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 b3c54a6..645456c 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -396,6 +396,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;
 	MyPgXact->delayChkpt = false;
 	MyPgXact->vacuumFlags = 0;
@@ -578,6 +579,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;
 	MyPgXact->delayChkpt = false;
 	MyPgXact->vacuumFlags = 0;
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 07f3c93..cee8f9e 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -654,6 +654,12 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 		 */
 		if (zero_damaged_pages || InRecovery)
 			MemSet(buffer, 0, BLCKSZ);
+		else if(SmgrIsTemp(reln) && blocknum == 0 && forknum == MAIN_FORKNUM)
+		{
+			/* global temp table init btree meta page */
+			MemSet(buffer, 0, BLCKSZ);
+			mdwrite(reln, forknum, blocknum, buffer, true);
+		}
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index a87e721..adce760 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -1008,6 +1008,10 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 				Assert(backend != InvalidBackendId);
 			}
 			break;
+		/* For global temp table */
+		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 35a8995..42dc3a5 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -141,6 +141,7 @@
 #include "utils/timestamp.h"
 #include "utils/typcache.h"
 
+#include "catalog/storage_gtt.h"
 
 /* Hooks for plugins to get control when we ask for stats */
 get_relation_stats_hook_type get_relation_stats_hook = NULL;
@@ -4568,12 +4569,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))
 							{
@@ -4652,15 +4666,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))
 		{
@@ -5972,6 +5998,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;
@@ -5989,6 +6016,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,
@@ -6000,6 +6034,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;
@@ -6015,6 +6051,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,
@@ -6881,6 +6924,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))
@@ -6893,6 +6938,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 =
@@ -6905,6 +6958,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.
@@ -6924,6 +6979,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 27602fa..25a411a 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -34,6 +34,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"
@@ -46,6 +47,7 @@
 #include "utils/syscache.h"
 #include "utils/typcache.h"
 
+
 /* Hook for plugins to get control in get_attavgwidth() */
 get_attavgwidth_hook_type get_attavgwidth_hook = NULL;
 
@@ -2878,6 +2880,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 585dcee..b8f2a41 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1130,6 +1130,16 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 				relation->rd_islocaltemp = false;
 			}
 			break;
+		case RELPERSISTENCE_GLOBAL_TEMP:
+			{
+				relation->rd_backend = BackendIdForTempRelations();
+				/*
+				 * For global temp table, all backend can use
+				 * this relation, so rd_islocaltemp always true.
+				 */
+				relation->rd_islocaltemp = true;
+			}
+			break;
 		default:
 			elog(ERROR, "invalid relpersistence: %c",
 				 relation->rd_rel->relpersistence);
@@ -3311,6 +3321,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 = true;
+			break;
 		default:
 			elog(ERROR, "invalid relpersistence: %c", relpersistence);
 			break;
@@ -3425,6 +3439,9 @@ RelationSetNewRelfilenode(Relation relation, char persistence)
 	TransactionId freezeXid = InvalidTransactionId;
 	RelFileNode newrnode;
 
+	if (RELATION_IS_GLOBAL_TEMP(relation))
+		elog(ERROR, "global temp table does not allow setting new relfilenode");
+
 	/* Allocate a new relfilenode */
 	newrelfilenode = GetNewRelFileNode(relation->rd_rel->reltablespace, NULL,
 									   persistence);
@@ -3465,7 +3482,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;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e84c8cc..3b921cd 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -138,6 +138,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,
@@ -1964,6 +1976,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."),
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index bf69adc..72e291b 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15585,6 +15585,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
 	{
 		char	   *ftoptions = NULL;
 		char	   *srvname = NULL;
+		char		*table_type = NULL;
 
 		switch (tbinfo->relkind)
 		{
@@ -15636,9 +15637,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);
 
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 090b6ba..34b4683 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -165,6 +165,7 @@ typedef FormData_pg_class *Form_pg_class;
 #define		  RELPERSISTENCE_PERMANENT	'p' /* regular table */
 #define		  RELPERSISTENCE_UNLOGGED	'u' /* unlogged permanent table */
 #define		  RELPERSISTENCE_TEMP		't' /* temporary table */
+#define		  RELPERSISTENCE_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/storage.h b/src/include/catalog/storage.h
index 3579d3f..2bde386 100644
--- a/src/include/catalog/storage.h
+++ b/src/include/catalog/storage.h
@@ -19,7 +19,7 @@
 #include "storage/smgr.h"
 #include "utils/relcache.h"
 
-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 RelationTruncate(Relation rel, BlockNumber nblocks);
diff --git a/src/include/catalog/storage_gtt.h b/src/include/catalog/storage_gtt.h
new file mode 100644
index 0000000..ea41e66
--- /dev/null
+++ b/src/include/catalog/storage_gtt.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 "utils/relcache.h"
+
+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);
+extern bool is_other_backend_use_gtt(RelFileNode node);
+extern bool gtt_storage_attached(Oid relid);
+extern Bitmapset *copy_active_gtt_bitmap(RelFileNode node);
+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 void get_gtt_relstats(Oid relid, BlockNumber *relpages, double *reltuples,
+							BlockNumber *relallvisible, TransactionId *relfrozenxid,
+							TransactionId *relminmxid);
+
+#endif							/* STORAGE_H */
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index f7e0781..4f5a353 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -130,4 +130,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/lwlock.h b/src/include/storage/lwlock.h
index f627dfe..cfc6c78 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 281e1db..2ab8e91 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 da8b672..8e7d4c7 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -124,4 +124,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 9aa3d02..1493cf4 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -278,6 +278,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 8b8b237..c1962b7 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -272,6 +272,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
@@ -530,7 +531,8 @@ 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
@@ -609,6 +611,17 @@ typedef struct ViewOptions
  */
 #define RelationGetPartitionDesc(relation) ((relation)->rd_partdesc)
 
+/* 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->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..50ca9ac
--- /dev/null
+++ b/src/test/regress/expected/gtt_clean.out
@@ -0,0 +1,7 @@
+reset search_path;
+drop schema gtt cascade;
+NOTICE:  drop cascades to 4 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
diff --git a/src/test/regress/expected/gtt_error.out b/src/test/regress/expected/gtt_error.out
new file mode 100644
index 0000000..80c16dc
--- /dev/null
+++ b/src/test/regress/expected/gtt_error.out
@@ -0,0 +1,106 @@
+CREATE SCHEMA IF NOT EXISTS gtt_error;
+set search_path=gtt_error,sys;
+create global temp table gtt1(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
+);
+CREATE global temp TABLE products (
+    product_no integer PRIMARY KEY,
+    name text,
+    price numeric
+);
+-- ERROR
+create index CONCURRENTLY idx_gtt1 on gtt1 (b);
+ERROR:  cannot create indexes on global temporary tables using concurrent mode
+-- ERROR
+cluster gtt1 using gtt1_pkey;
+ERROR:  not support cluster global temporary tables yet
+-- 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
+create table gtt1(a int primary key, b text) with(on_commit_delete_rows=true);
+ERROR:  regular table cannot specifie on_commit_delete_rows
+-- ERROR
+CREATE global temp TABLE measurement (
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+ERROR:  not support create global temporary partition table yet
+-- ERROR
+create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent);
+ERROR:  not support create global temporary inheritance table yet
+-- 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:  can not defeine global temp 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);
+--ERROR
+alter table gtt1 rename to gttx;
+ERROR:  not support rename global temporary tables yet
+-- ERROR
+ALTER TABLE gtt1 ADD COLUMN address varchar(30);
+ERROR:  not support alter global temporary tables yet
+-- ERROR
+CREATE TABLE orders (
+    order_id integer PRIMARY KEY,
+    product_no integer REFERENCES products (product_no),
+    quantity integer
+);
+ERROR:  referenced relation "products" is not a global temp table
+-- ERROR
+CREATE global temp TABLE orders (
+    order_id integer PRIMARY KEY,
+    product_no integer REFERENCES products (product_no),
+    quantity integer
+);
+ERROR:  not support alter global temporary tables yet
+-- ERROR
+CREATE GLOBAL TEMPORARY TABLE mytable (
+  id SERIAL PRIMARY KEY,
+  data text
+) on commit preserve rows;
+ERROR:  Global temp table does not yet support serial column
+-- ERROR
+create global temp table gttx(id int GENERATED ALWAYS AS IDENTITY (START WITH 2));
+ERROR:  Global temp table does not yet support serial column
+--ERROR
+CREATE MATERIALIZED VIEW mv_gtt1 as select * from gtt1;
+ERROR:  materialized views must not use global temporary tables or views
+-- ALL ERROR
+create index idx_err on gtt1 using hash (a);
+ERROR:  only support btree index on global temp table
+create index idx_err on gtt1 using gist (a);
+ERROR:  data type integer has no default operator class for access method "gist"
+HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+create index idx_tmp_t0_1 on tmp_t0 using gin (c0);
+ERROR:  only support btree index on global temp table
+create index idx_tmp_t0_1 on tmp_t0 using gist (c0);
+ERROR:  only support btree index on global temp table
+reset search_path;
+drop schema gtt_error cascade;
+NOTICE:  drop cascades to 8 other objects
+DETAIL:  drop cascades to table gtt_error.gtt1
+drop cascades to table gtt_error.gtt2
+drop cascades to table gtt_error.gtt3
+drop cascades to table gtt_error.tmp_t0
+drop cascades to table gtt_error.tbl_inherits_parent
+drop cascades to table gtt_error.tbl_inherits_parent_global_temp
+drop cascades to table gtt_error.products
+drop cascades to table gtt_error.gtt5
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..30d8a7b
--- /dev/null
+++ b/src/test/regress/expected/gtt_parallel_1.out
@@ -0,0 +1,84 @@
+set search_path=gtt,sys;
+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..70bf4a6
--- /dev/null
+++ b/src/test/regress/expected/gtt_parallel_2.out
@@ -0,0 +1,142 @@
+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 
+---+---
+(0 rows)
+
+begin;
+select * from gtt3 order by a;
+ a | b 
+---+---
+(0 rows)
+
+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(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);
+insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',10000);
+select relname, pg_relation_size(oid),pg_relation_size(reltoastrelid) from pg_class where relname = 'gtt_t_kenyon';
+   relname    | pg_relation_size | pg_relation_size 
+--------------+------------------+------------------
+ gtt_t_kenyon |           450560 |            16384
+(1 row)
+
+select relname from pg_class where relname = 'gtt_t_kenyon' and reltoastrelid != 0;
+   relname    
+--------------
+ gtt_t_kenyon
+(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..9e8f5f0
--- /dev/null
+++ b/src/test/regress/expected/gtt_prepare.out
@@ -0,0 +1,7 @@
+CREATE SCHEMA IF NOT EXISTS gtt;
+set search_path=gtt,sys;
+create global temp table gtt1(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 gtt_t_kenyon(id int,vname varchar(48),remark text) on commit PRESERVE rows;
+reset search_path;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index fc0f141..6836eeb 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -121,3 +121,9 @@ test: fast_default
 
 # run stats by itself because its delay may be insufficient under heavy load
 test: stats
+
+# global temp table test
+test: gtt_error
+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..f3cf710
--- /dev/null
+++ b/src/test/regress/sql/gtt_clean.sql
@@ -0,0 +1,6 @@
+
+
+reset search_path;
+
+drop schema gtt cascade;
+
diff --git a/src/test/regress/sql/gtt_error.sql b/src/test/regress/sql/gtt_error.sql
new file mode 100644
index 0000000..e895574
--- /dev/null
+++ b/src/test/regress/sql/gtt_error.sql
@@ -0,0 +1,106 @@
+
+CREATE SCHEMA IF NOT EXISTS gtt_error;
+
+set search_path=gtt_error,sys;
+
+create global temp table gtt1(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
+);
+
+CREATE global temp TABLE products (
+    product_no integer PRIMARY KEY,
+    name text,
+    price numeric
+);
+
+-- 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
+create table gtt1(a int primary key, b text) with(on_commit_delete_rows=true);
+
+-- ERROR
+CREATE global temp TABLE measurement (
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+
+-- ERROR
+create global temp table tbl_inherits_partition() inherits (tbl_inherits_parent);
+
+-- 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);
+
+--ERROR
+alter table gtt1 rename to gttx;
+
+-- ERROR
+ALTER TABLE gtt1 ADD COLUMN address varchar(30);
+
+-- ERROR
+CREATE TABLE orders (
+    order_id integer PRIMARY KEY,
+    product_no integer REFERENCES products (product_no),
+    quantity integer
+);
+
+-- ERROR
+CREATE global temp TABLE orders (
+    order_id integer PRIMARY KEY,
+    product_no integer REFERENCES products (product_no),
+    quantity integer
+);
+
+-- ERROR
+CREATE GLOBAL TEMPORARY TABLE mytable (
+  id SERIAL PRIMARY KEY,
+  data text
+) on commit preserve rows;
+
+-- ERROR
+create global temp table gttx(id int GENERATED ALWAYS AS IDENTITY (START WITH 2));
+
+--ERROR
+CREATE MATERIALIZED VIEW mv_gtt1 as select * from gtt1;
+
+-- ALL ERROR
+create index idx_err on gtt1 using hash (a);
+create index idx_err on gtt1 using gist (a);
+create index idx_tmp_t0_1 on tmp_t0 using gin (c0);
+create index idx_tmp_t0_1 on tmp_t0 using gist (c0);
+
+reset search_path;
+
+drop schema gtt_error 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..d7d81de
--- /dev/null
+++ b/src/test/regress/sql/gtt_parallel_1.sql
@@ -0,0 +1,42 @@
+
+
+set search_path=gtt,sys;
+
+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..cb2f7a6
--- /dev/null
+++ b/src/test/regress/sql/gtt_parallel_2.sql
@@ -0,0 +1,62 @@
+
+
+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(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);
+insert into gtt_t_kenyon select generate_series(1,2),repeat('kenyon here'||'^_^',2),repeat('^_^ Kenyon is not God,Remark here!!',10000);
+select relname, pg_relation_size(oid),pg_relation_size(reltoastrelid) from pg_class where relname = 'gtt_t_kenyon';
+select relname from pg_class where relname = 'gtt_t_kenyon' and reltoastrelid != 0;
+
+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..042d9e6
--- /dev/null
+++ b/src/test/regress/sql/gtt_prepare.sql
@@ -0,0 +1,15 @@
+
+CREATE SCHEMA IF NOT EXISTS gtt;
+
+set search_path=gtt,sys;
+
+create global temp table gtt1(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 gtt_t_kenyon(id int,vname varchar(48),remark text) on commit PRESERVE rows;
+
+reset search_path;
+
--
libgit2 0.23.3