global_temporary_table_v1-pg13.patch
text/x-patch
Filename: global_temporary_table_v1-pg13.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v1
| 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 | 810 | 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_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 |
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..f623626 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) ||
+ gtt_storage_attached(heapRelation->rd_node.relNode))
+ elog(ERROR, "can not create index when have one or more 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 */
+ 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..c7b9a48
--- /dev/null
+++ b/src/backend/catalog/storage_gtt.c
@@ -0,0 +1,810 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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(>t_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(>t_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(>t_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(>t_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(>t_shared_ctl->lock, LW_EXCLUSIVE);
+
+ entry = hash_search(active_gtt_shared_hash,
+ (void *) &(rnode), HASH_FIND, NULL);
+
+ if (entry == NULL)
+ {
+ if (!skiplock)
+ LWLockRelease(>t_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(>t_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(>t_shared_ctl->lock, LW_SHARED);
+ entry = hash_search(active_gtt_shared_hash,
+ (void *) &(node), HASH_FIND, NULL);
+
+ if (entry == NULL)
+ {
+ LWLockRelease(>t_shared_ctl->lock);
+ return NULL;
+ }
+
+ Assert(entry->map);
+ if (!bms_is_empty(entry->map))
+ map_copy = bms_copy(entry->map);
+
+ LWLockRelease(>t_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(>t_shared_ctl->lock, LW_SHARED);
+ entry = hash_search(active_gtt_shared_hash,
+ (void *) &(node), HASH_FIND, NULL);
+
+ if (entry == NULL)
+ {
+ LWLockRelease(>t_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(>t_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(>t_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(>t_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; /* it belongs after 'prev', before 'curr' */
+
+ i += 1;
+ }
+ /* Insert datum into list after 'prev' */
+ list_insert_nth_oid(gtt_session_relfrozenxid_list, i, relfrozenxid);
+ MemoryContextSwitchTo(oldcontext);
+}
+
+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 31a5ef0..518d0fa 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,
@@ -1962,6 +1974,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 6791e0c..82b672f 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -276,6 +276,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_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;
+