Subject: Re: [Proposal] Global temporary tables

From: Justin Pryzby <[email-masked]>
Date: Sat, 26 Feb 2022 18:21:12 -0600

I read through this.
Find attached some language fixes.  You should be able to apply each "fix"
patch on top of your own local branch with git am, and then squish them
together.  Let me know if you have trouble with that.

I think get_seqence_start_value() should be static.  (Or otherwise, it should
be in lsyscache.c).

The include added to execPartition.c seems to be unused.

+#define RELATION_IS_TEMP_ON_CURRENT_SESSION(relation) \
+#define RELATION_IS_TEMP(relation) \
+#define RelpersistenceTsTemp(relpersistence) \
+#define RELATION_GTT_ON_COMMIT_DELETE(relation)    \

=> These macros can evaluate their arguments multiple times.
You should add a comment to warn about that.  And maybe avoid passing them a
function argument, like: RelpersistenceTsTemp(get_rel_persistence(rte->relid))

+list_all_backend_gtt_frozenxids should return TransactionId not int.
The function name should say "oldest" and not "all" ?

I think the GUC should have a longer name.  max_active_gtt is too short for a
global var.

+#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

It's being initialized to MIN, but then the GUC machinery sets it to DEFAULT.
By convention, it should be initialized to default.

fout->remoteVersion >= 140000

=> should say 15

describe.c has gettext_noop("session"), which is a half-truth.  The data is
per-session but the table definition is persistent..

You redirect stats from pg_class and pg_statistics to a local hash table.
This is pretty hairy :(
I guess you'd also need to handle pg_statistic_ext and ext_data.
pg_stats doesn't work, since the data isn't in pg_statistic - it'd need to look
at pg_get_gtt_statistics.

I wonder if there's a better way to do it, like updating pg_statistic but
forcing the changes to be rolled back when the session ends...  But I think
that would make longrunning sessions behave badly, the same as "longrunning
transactions".

Have you looked at Gilles Darold's GTT extension ?
>From b89f3cc5c78e7b4c3e10ab39ef527b524d0d112d Mon Sep 17 00:00:00 2001
From: Justin Pryzby <[email-masked]>
Date: Sat, 1 Jan 2022 17:02:30 -0600
Subject: [PATCH 02/11] f!0002-gtt-v64-doc.patch

---
 doc/src/sgml/ref/create_table.sgml | 42 ++++++++++++++++--------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 408373323c..9cd5fc17f2 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -174,18 +174,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
      <para>
       If specified, the table is created as a temporary table.
       Optionally, <literal>GLOBAL</literal> or <literal>LOCAL</literal>
-      can be written before <literal>TEMPORARY</literal> or <literal>TEMP</literal>.
+      may be written before <literal>TEMPORARY</literal> or <literal>TEMP</literal>.
       They represent two types of temporary tables supported by <productname>PostgreSQL</productname>:
-      global temporary table and local temporary table. Without specified
-      GLOBAL or LOCAL, a local temporary table is created by default.
+      global temporary table and local temporary table. If neither
+      GLOBAL or LOCAL is specified, a local temporary table is created by default.
      </para>
 
     <para>
-     Both types of temporary tables’ data are truncated at the
+     Data of both types of temporary tables is truncated at the
      end of a session or optionally at the end of the current transaction.
-     (see <literal>ON COMMIT</literal> below). For global temporary table,
-     its schema is reserved and reused by future sessions or transactions.
-     For local temporary table, both its data and its schema are dropped.
+     (see <literal>ON COMMIT</literal> below). For a global temporary table,
+     its table definition is preserved and reused by future sessions or transactions.
+     For a local temporary table, both its data and its table definition are dropped.
     </para>
 
     <variablelist>
@@ -194,10 +194,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
       <listitem>
        <para>
         Global temporary table are defined just once and automatically exist
-        (starting with empty contents) in every session that needs them.
-        The schema definition of temporary tables is persistent and shared among sessions.
-        However, the data in temporary tables are kept private to sessions themselves,
-        even though they use same name and same schema.
+        (initially with empty contents) in every session.
+        The schema definition of a temporary table is persistent and common to all sessions.
+        However, the data in temporary tables is private to each sessions,
+        even though they share the same name and same table definition.
        </para>
       </listitem>
      </varlistentry>
@@ -206,15 +206,15 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
       <term><literal>Local Temporary Table</literal></term>
      <listitem>
      <para>
-      Local temporary table are automatically dropped at the end of a
-      session (include schema and data). Future sessions need to create
-      their own temporary tables when they are used.
+      Local temporary tables are automatically dropped at the end of a
+      session (include table definition and data). Future sessions need to create
+      their own temporary tables before using them.
      </para>
      <para>
-      The default search_path includes the temporary schema first and so
-      identically named existing permanent tables are not chosen for new plans
-      while the temporary table exists, unless they are referenced
-      with schema-qualified names. Any indexes created on a temporary
+      The default <varname>search_path</varname> includes the temporary schema first, and so
+      identically-named, permanent tables of the same name as a temporary table are not chosen for new plans
+      so long as the temporary table exists, unless the permanent table is referenced
+      with a schema-qualified name. Any indexes created on a temporary
       table are automatically temporary as well.
      </para>
      </listitem>
@@ -229,9 +229,11 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
      table is going to be used in complex queries, it is wise to run
      <command>ANALYZE</command> on the temporary table after it is populated.
     </para>
+
     <para>
-     The Temporary table resembles the SQL standard, but has some differences.
-     see <xref linkend="sql-createtable-compatibility"/> below.
+     The temporary table resembles the behavior defined in the SQL standard,
+     but has some differences.
+     See <xref linkend="sql-createtable-compatibility"/> below.
     </para>
 
     </listitem>
-- 
2.17.1

>From 63d4a62043bd1d2af381c632553f85c89de4f36f Mon Sep 17 00:00:00 2001
From: Justin Pryzby <[email-masked]>
Date: Sat, 1 Jan 2022 16:47:14 -0600
Subject: [PATCH 04/11] f!0003-gtt-v64-implementation.patch

---
 src/backend/catalog/heap.c          |  8 ++++----
 src/backend/catalog/index.c         | 24 +++++++++++-----------
 src/backend/catalog/storage_gtt.c   | 32 ++++++++++++++---------------
 src/backend/commands/analyze.c      |  4 ++--
 src/backend/commands/cluster.c      |  6 +++---
 src/backend/commands/sequence.c     |  6 +++---
 src/backend/commands/tablecmds.c    | 10 ++++-----
 src/backend/commands/vacuum.c       |  2 +-
 src/backend/commands/view.c         |  2 +-
 src/backend/parser/analyze.c        |  2 +-
 src/backend/parser/parse_utilcmd.c  |  2 +-
 src/backend/postmaster/autovacuum.c |  4 ++--
 src/backend/storage/ipc/procarray.c |  5 ++++-
 src/backend/utils/adt/dbsize.c      |  2 +-
 src/bin/pg_dump/pg_dump.c           |  2 +-
 src/include/catalog/pg_class.h      |  2 +-
 16 files changed, 58 insertions(+), 55 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 8e5b56f602..d24afa45c1 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2023,15 +2023,15 @@ heap_drop_with_catalog(Oid relid)
 		update_default_partition_oid(parentOid, InvalidOid);
 
 	/*
-	 * Only when other sessions are not using this Global temporary table,
-	 * is it allowed to DROP it.
+	 * It is not allowed to DROP a Global temporary table when other
+	 * sessions are using it
 	 */
 	if (RELATION_IS_GLOBAL_TEMP(rel))
 	{
 		if (is_other_backend_use_gtt(RelationGetRelid(rel)))
 			ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot drop global temporary table %s when other backend attached it.",
+				 errmsg("cannot drop global temporary table %s being accessed by another backend",
 						RelationGetRelationName(rel))));
 	}
 
@@ -3466,7 +3466,7 @@ heap_truncate_one_rel(Relation rel)
 
 	/*
 	 * After the data is cleaned up on the GTT, the transaction information
-	 * for the data(stored in local hash table) is also need reset.
+	 * for the data(stored in local hash table) also needs to be reset.
 	 */
 	if (RELATION_IS_GLOBAL_TEMP(rel))
 		up_gtt_relstats(RelationGetRelid(rel), 0, 0, 0, RecentXmin, GetOldestMultiXactId());
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index be08ba376a..e2483426d2 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -741,11 +741,11 @@ index_create(Relation heapRelation,
 	/* For global temporary table only */
 	if (RELATION_IS_GLOBAL_TEMP(heapRelation))
 	{
-		/* No support create index on global temporary table with concurrent mode */
+		/* No support for creating index on global temporary table with concurrent mode */
 		Assert(!concurrent);
 
 		/*
-		 * For the case that some backend is applied relcache message to create
+		 * For the case that some backend is applied relcache message to create XXX
 		 * an index on a global temporary table, if this table in the current
 		 * backend are not initialized, the creation of index storage on the
 		 * table are also skipped.
@@ -2201,18 +2201,18 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode)
 	CheckTableNotInUse(userIndexRelation, "DROP INDEX");
 
 	/*
-	 * Allow to drop index on global temporary table when only current
-	 * backend use it.
+	 * Disallow dropping index on global temporary table if another
+	 * backend is using it.
 	 */
 	if (RELATION_IS_GLOBAL_TEMP(userHeapRelation) &&
 		is_other_backend_use_gtt(RelationGetRelid(userHeapRelation)))
 	{
-		ereport(ERROR,
+		ereport(ERROR, // XXX
 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 			 errmsg("cannot drop index %s on global temporary table %s",
 					RelationGetRelationName(userIndexRelation), RelationGetRelationName(userHeapRelation)),
-					errdetail("Because the index is created on the global temporary table and other backend attached it."),
-					errhint("Please try detach all sessions using this temporary table and try again.")));
+					errdetail("The index is created on the global temporary table and other backend attached it."),
+					errhint("Detach other sessions using this temporary table and try again.")));
 	}
 
 	/*
@@ -2926,12 +2926,12 @@ index_update_stats(Relation rel,
 		/* For global temporary table */
 		if (is_gtt)
 		{
-			/* Update GTT'statistics into local relcache */
+			/* Update GTT's statistics into local relcache */
 			rel->rd_rel->relpages = (int32) relpages;
 			rel->rd_rel->reltuples = (float4) reltuples;
 			rel->rd_rel->relallvisible = (int32) relallvisible;
 
-			/* Update GTT'statistics into local hashtable */
+			/* Update GTT's statistics into local hashtable */
 			up_gtt_relstats(RelationGetRelid(rel), relpages, reltuples, relallvisible,
 							InvalidTransactionId, InvalidMultiXactId);
 		}
@@ -3066,7 +3066,7 @@ index_build(Relation heapRelation,
 		pgstat_progress_update_multi_param(6, progress_index, progress_vals);
 	}
 
-	/* For build index on global temporary table */
+	/* If building index on global temporary table */
 	if (RELATION_IS_GLOBAL_TEMP(indexRelation))
 	{
 		/*
@@ -3075,7 +3075,7 @@ index_build(Relation heapRelation,
 		 */
 		if (!gtt_storage_attached(RelationGetRelid(indexRelation)))
 		{
-			/* Before create init storage, fix the local Relcache first */
+			/* Before creating init storage, fix the local Relcache first */
 			force_enable_gtt_index(indexRelation);
 
 			Assert(gtt_storage_attached(RelationGetRelid(heapRelation)));
@@ -3651,7 +3651,7 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
 		if (OidIsValid(params->tablespaceOid))
 			ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("The tablespace of global temporary table can not be changed")));
+				 errmsg("The tablespace of global temporary table cannot be changed")));
 
 		if (!gtt_storage_attached(indexId))
 		{
diff --git a/src/backend/catalog/storage_gtt.c b/src/backend/catalog/storage_gtt.c
index d50e1fa9af..1660163e99 100644
--- a/src/backend/catalog/storage_gtt.c
+++ b/src/backend/catalog/storage_gtt.c
@@ -1,12 +1,12 @@
 /*-------------------------------------------------------------------------
  *
  * storage_gtt.c
- *	  The body implementation of Global Temparary table.
+ *	  The body implementation of Global Temporary table.
  *
  * IDENTIFICATION
  *	  src/backend/catalog/storage_gtt.c
  *
- *	  See src/backend/catalog/GTT_README for Global temparary table's
+ *	  See src/backend/catalog/GTT_README for Global temporary table's
  *	  requirements and design.
  *
  *-------------------------------------------------------------------------
@@ -197,7 +197,7 @@ active_gtt_shared_hash_size(void)
 }
 
 /*
- * Initialization shared hash table for GTT.
+ * Initialization of shared hash table for GTT.
  */
 void
 active_gtt_shared_hash_init(void)
@@ -277,7 +277,7 @@ gtt_storage_checkin(Oid relid)
 
 /*
  * Remove the GTT relid record from the shared hash table which means that current backend is
- * not use this GTT.
+ * not using this GTT.
  */
 static void
 gtt_storage_checkout(Oid relid, bool isCommit)
@@ -298,7 +298,7 @@ gtt_storage_checkout(Oid relid, bool isCommit)
 	{
 		LWLockRelease(&gtt_shared_ctl->lock);
 		if (isCommit)
-			elog(WARNING, "relid %u not exist in gtt shared hash when drop local storage", relid);
+			elog(WARNING, "relid %u doesn't exist in gtt shared hash when dropping local storage", relid);
 
 		return;
 	}
@@ -319,9 +319,9 @@ gtt_storage_checkout(Oid relid, bool isCommit)
 
 /*
  * Gets usage information for a GTT from shared hash table.
- * The information is in the form of bitmap.
- * Quickly copy the entire bitmap from shared memory and return it.
- * that to avoid holding locks for a long time.
+ * The information is in the form of a bitmap.
+ * Quickly copy the entire bitmap from shared memory and return it,
+ * to avoid holding locks for a long time.
  */
 static Bitmapset *
 copy_active_gtt_bitmap(Oid relid)
@@ -417,18 +417,18 @@ remember_gtt_storage_info(RelFileNode rnode, Relation rel)
 	if (max_active_gtt <= 0)
 		ereport(ERROR,
 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			 errmsg("Global temporary table feature is disable"),
+			 errmsg("Global temporary table feature is disabled"),
 			 errhint("You might need to increase max_active_global_temporary_table to enable this feature.")));
 
 	if (RecoveryInProgress())
-		elog(ERROR, "readonly mode not support access global temporary table");
+		elog(ERROR, "recovery mode does not support accessing global temporary table");
 
 	if (rel->rd_rel->relkind == RELKIND_INDEX &&
 		rel->rd_index &&
 		(!rel->rd_index->indisvalid ||
 		 !rel->rd_index->indisready ||
 		 !rel->rd_index->indislive))
-		 elog(ERROR, "invalid gtt index %s not allow to create storage", RelationGetRelationName(rel));
+		 elog(ERROR, "invalid gtt index %s not allowed to create storage", RelationGetRelationName(rel));
 
 	/* First time through: initialize the hash table */
 	if (gtt_storage_local_hash == NULL)
@@ -757,7 +757,7 @@ up_gtt_relstats(Oid relid,
 
 /*
  * Search GTT relstats(relpage/reltuple/relallvisible)
- * from local has.
+ * from local hash.
  */
 bool
 get_gtt_relstats(Oid relid, BlockNumber *relpages, double *reltuples,
@@ -852,7 +852,7 @@ up_gtt_att_statistic(Oid reloid, int attnum, bool inh, int natts,
 	}
 	MemoryContextSwitchTo(oldcontext);
 
-	if (!found)
+	if (!found) // XXX: should be an ereport with a hint ?
 		elog(WARNING, "analyze can not update relid %u column %d statistics after add or drop column, try truncate table first", reloid, attnum);
 
 	return;
@@ -960,7 +960,7 @@ remove_gtt_relfrozenxid_from_ordered_list(Oid relfrozenxid)
 
 /*
  * Update of backend Level oldest relfrozenxid to MyProc.
- * This makes each backend's oldest RelFrozenxID globally visible.
+ * This makes each backend's oldest RelFrozenXID globally visible.
  */
 static void
 set_gtt_session_relfrozenxid(void)
@@ -1282,7 +1282,7 @@ pg_list_gtt_relfrozenxids(PG_FUNCTION_ARGS)
 }
 
 /*
- * In order to build the GTT index, force enable GTT'index.
+ * In order to build the GTT index, force enable GTT index.
  */
 void
 force_enable_gtt_index(Relation index)
@@ -1331,7 +1331,7 @@ gtt_fix_index_backend_state(Relation index)
 
 /*
  * During the SQL initialization of the executor (InitPlan)
- * Initialize storage of GTT GTT'indexes and build empty index.
+ * Initialize storage of GTT's indexes and build empty index.
  */
 void
 init_gtt_storage(CmdType operation, Relation relation)
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 72218b2c66..6dee51784f 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -188,8 +188,8 @@ analyze_rel(Oid relid, RangeVar *relation,
 	}
 
 	/*
-	 * Skip the global temporary table that did not initialize the storage
-	 * in this backend.
+	 * Skip global temporary tables for which storage was not initialized by
+	 * this backend.
 	 */
 	if (RELATION_IS_GLOBAL_TEMP(onerel) &&
 		!gtt_storage_attached(RelationGetRelid(onerel)))
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 535f537aae..ff6ae583f5 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -392,15 +392,15 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
 	}
 
 	/*
-	 * Skip the global temporary table that did not initialize the storage
-	 * in this backend.
+	 * Skip global temporary tables for which storage was not initialized by
+	 * this backend.
 	 */
 	if (RELATION_IS_GLOBAL_TEMP(OldHeap))
 	{
 		if (gtt_storage_attached(RelationGetRelid(OldHeap)))
 			ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("not support cluster global temporary table")));
+				 errmsg("clustering is not supported on a global temporary table")));
 
 		relation_close(OldHeap, AccessExclusiveLock);
 		pgstat_progress_end_command();
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index af12dd2cdc..4d5aedf2c3 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -461,7 +461,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
 		if (is_other_backend_use_gtt(RelationGetRelid(seqrel)))
 			ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter global temporary sequence %s when other backend attached it.",
+				 errmsg("cannot alter global temporary sequence %s being accessed by another backend",
 						RelationGetRelationName(seqrel))));
 	}
 
@@ -1194,7 +1194,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
 	*p_elm = elm;
 	*p_rel = seqrel;
 
-	/* Initializes the storage for sequence which the global temporary table belongs. */
+	/* Initializes the storage for sequence which belongs to a global temporary table. */
 	if (RELATION_IS_GLOBAL_TEMP(seqrel) &&
 		!gtt_storage_attached(RelationGetRelid(seqrel)))
 	{
@@ -1999,7 +1999,7 @@ get_seqence_start_value(Oid seqid)
 }
 
 /*
- * Initialize sequence which global temporary table belongs.
+ * Initialize sequence which belongs to a global temporary table.
  */
 void
 gtt_init_seq(Relation rel)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7aa3beba2c..e4b67f8731 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2018,8 +2018,8 @@ ExecuteTruncateGuts(List *explicit_rels,
 		}
 
 		/*
-		 * Skip the global temporary table that is not initialized for storage
-		 * in current backend.
+		 * Skip global temporary tables for which the current backend has not
+		 * initialized storage.
 		 */
 		if (RELATION_IS_GLOBAL_TEMP(rel))
 		{
@@ -3365,7 +3365,7 @@ CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
 	if (RELATION_IS_GLOBAL_TEMP(rel))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("The tablespace of global temporary table can not be changed")));
+				 errmsg("The tablespace of global temporary table cannot be changed")));
 
 	return true;
 }
@@ -4136,13 +4136,13 @@ AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
 	/* Caller is required to provide an adequate lock. */
 	rel = relation_open(context->relid, NoLock);
 
-	/* We allow to alter global temporary table only current backend use it */
+	/* We disallow altering a global temporary table if another backend is using it */
 	if (RELATION_IS_GLOBAL_TEMP(rel))
 	{
 		if (is_other_backend_use_gtt(RelationGetRelid(rel)))
 			ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot alter global temporary table %s when other backend attached it.",
+				 errmsg("cannot alter global temporary table %s being accessed by another backend",
 						RelationGetRelationName(rel))));
 	}
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 47c734aa1c..3a48892feb 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2140,7 +2140,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
 	}
 
 	/*
-	 * Skip those global temporary table that are not initialized in
+	 * Skip global temporary tables that are not initialized in the
 	 * current backend.
 	 */
 	if (RELATION_IS_GLOBAL_TEMP(rel) &&
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index cd2bd57e38..79e4b0c9c7 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -543,7 +543,7 @@ DefineView(ViewStmt *stmt, const char *queryString,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("views cannot be unlogged because they do not have storage")));
 
-	/* Global temporary table are not sensible. */
+	/* Global temporary views are not sensible. */
 	if (stmt->view->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 		ereport(ERROR,
 				(errcode(ERRCODE_SYNTAX_ERROR),
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 75a0940b25..4b96429f30 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -2912,7 +2912,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
 		if (is_query_using_gtt(query))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("materialized views must not use global temporary tables or views")));
+					 errmsg("materialized views must not use global temporary tables")));
 
 		/*
 		 * A materialized view would either need to save parameters for use in
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 45241b8b0f..c9bef46339 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -449,7 +449,7 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
 
 	/*
 	 * If a sequence is bound to a global temporary table, then the sequence
-	 * must been "global temporary"
+	 * must be "global temporary"
 	 */
 	if (cxt->relation->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 		seqstmt->sequence->relpersistence = cxt->relation->relpersistence;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 5601f15d18..fa8c391d40 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2119,8 +2119,8 @@ do_autovacuum(void)
 		else if (classForm->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 		{
 			/*
-			 * Aotuvacuum cannot vacuum the private data stored in each backend
-			 * that belongs to global temporary table, so skip them.
+			 * Autovacuum cannot vacuum the private data stored in each backend
+			 * that belongs to global temporary tables, so skip them.
 			 */
 			continue;
 		}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index e9cea9b423..0b207dca16 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -5180,6 +5180,9 @@ KnownAssignedXidsReset(void)
 /*
  * search all active backend to get oldest frozenxid
  * for global temporary table.
+ * Return the oldest frozenxid, or InvalidTransactionId if none.
+ * If pids!=NULL, it's populated with qualifying backend pids.
+ * If xids!=NULL, it's populated with qualifying backend frozenxids.
  */
 int
 list_all_backend_gtt_frozenxids(int max_size, int *pids, uint32 *xids, int *n)
@@ -5216,7 +5219,7 @@ list_all_backend_gtt_frozenxids(int max_size, int *pids, uint32 *xids, int *n)
 	if (max_size > 0 && max_size < arrayP->numProcs)
 	{
 		LWLockRelease(ProcArrayLock);
-		elog(ERROR, "list_all_gtt_frozenxids require more array");
+		elog(ERROR, "list_all_gtt_frozenxids requires a longer array");
 	}
 
 	for (index = 0; index < arrayP->numProcs; index++)
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 9a6e7cee24..b86bc4fc69 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -984,7 +984,7 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 			break;
 		case RELPERSISTENCE_GLOBAL_TEMP:
 			/*
-			 * For global temporary table ,each backend has its own storage,
+			 * For global temporary table, each backend has its own storage,
 			 * also only sees its own storage. Use Backendid to identify them.
 			 */
 			backend = BackendIdForTempRelations();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 3100ddc704..caab5dc36a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15431,7 +15431,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
 
 		/*
 		 * Transaction information for the global temporary table is not stored
-		 * in the pg_class.
+		 * in pg_class.
 		 */
 		if (tbinfo->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
 		{
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index e23cb49c01..658e9be8de 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -169,10 +169,10 @@ DECLARE_INDEX(pg_class_tblspc_relfilenode_index, 3455, ClassTblspcRelfilenodeInd
 #define		  RELKIND_PARTITIONED_TABLE 'p' /* partitioned table */
 #define		  RELKIND_PARTITIONED_INDEX 'I' /* partitioned index */
 
+#define		  RELPERSISTENCE_GLOBAL_TEMP	'g' /* global temporary table */
 #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'
-- 
2.17.1

>From 8311d1d40294865a419b9bd0950c3cd642744e28 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <[email-masked]>
Date: Sat, 1 Jan 2022 16:46:54 -0600
Subject: [PATCH 06/11] f!0004-gtt-v64-regress.patch

---
 src/test/regress/expected/global_temporary_table.out | 12 ++++++------
 src/test/regress/sql/global_temporary_table.sql      |  4 ++--
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/test/regress/expected/global_temporary_table.out b/src/test/regress/expected/global_temporary_table.out
index 2ada5fa6b9..0aeb53d46d 100644
--- a/src/test/regress/expected/global_temporary_table.out
+++ b/src/test/regress/expected/global_temporary_table.out
@@ -1,5 +1,5 @@
 --
--- GLobal emparary table test case 
+-- Global temporary table test case 
 --
 CREATE SCHEMA IF NOT EXISTS global_temporary_table;
 set search_path=global_temporary_table,sys;
@@ -153,16 +153,16 @@ ERROR:  Only support alter global temporary table in an empty context.
 HINT:  Please create a new connection and execute ALTER TABLE on the new connection.
 -- should fail
 cluster gtt_on_commit_default using gtt_on_commit_default_pkey;
-ERROR:  not support cluster global temporary table
+ERROR:  clustering is not supported on a global temporary table
 -- should fail
 create table gtt_on_commit_default(a int primary key, b text) on commit delete rows;
 ERROR:  ON COMMIT can only be used on temporary tables
 -- should fail
 REINDEX (TABLESPACE pg_default) TABLE gtt_test_createindex;
-ERROR:  The tablespace of global temporary table can not be changed
+ERROR:  The tablespace of global temporary table cannot be changed
 -- should fail
 REINDEX (TABLESPACE pg_default) INDEX gtt_idx_1;
-ERROR:  The tablespace of global temporary table can not be changed
+ERROR:  The tablespace of global temporary table cannot be changed
 -- should fail
 alter table gtt_on_commit_default set ( on_commit_delete_rows='true');
 ERROR:  table cannot add or modify on commit parameter by ALTER TABLE command.
@@ -184,7 +184,7 @@ create global temp table gtt4(a int primary key, b text) with(on_commit_delete_r
 ERROR:  could not create global temporary table with on commit and with clause at same time
 -- should fail
 CREATE MATERIALIZED VIEW mv_gtt_on_commit_default as select * from gtt_on_commit_default;
-ERROR:  materialized views must not use global temporary tables or views
+ERROR:  materialized views must not use global temporary tables
 -- test create table as select
 CREATE GLOBAL TEMPORARY TABLE test_create_table_as AS SELECT 1 AS a;
 -- test copy stmt
@@ -482,7 +482,7 @@ select * from pg_gtt_stats where tablename = 'temp_table_test_systemview' order
 (2 rows)
 
 -- get all object info in current schema
-select relname ,relkind, relpersistence, reloptions from pg_class c, pg_namespace n where c.relnamespace = n.oid and n.nspname = 'global_temporary_table' order by relkind,relpersistence,relname;
+select relname, relkind, relpersistence, reloptions from pg_class c, pg_namespace n where c.relnamespace = n.oid and n.nspname = 'global_temporary_table' order by relkind,relpersistence,relname;
                      relname                     | relkind | relpersistence |          reloptions           
 -------------------------------------------------+---------+----------------+-------------------------------
  global_temp_partition_01_2021_id_seq            | S       | g              | {on_commit_delete_rows=false}
diff --git a/src/test/regress/sql/global_temporary_table.sql b/src/test/regress/sql/global_temporary_table.sql
index 51cd9bbbc0..e47c6aa3cf 100644
--- a/src/test/regress/sql/global_temporary_table.sql
+++ b/src/test/regress/sql/global_temporary_table.sql
@@ -1,5 +1,5 @@
 --
--- GLobal emparary table test case 
+-- Global temporary table test case
 --
 
 CREATE SCHEMA IF NOT EXISTS global_temporary_table;
@@ -306,7 +306,7 @@ select schemaname, tablename, relpages, reltuples, relallvisible from pg_gtt_rel
 select * from pg_gtt_stats where tablename = 'temp_table_test_systemview' order by tablename;
 
 -- get all object info in current schema
-select relname ,relkind, relpersistence, reloptions from pg_class c, pg_namespace n where c.relnamespace = n.oid and n.nspname = 'global_temporary_table' order by relkind,relpersistence,relname;
+select relname, relkind, relpersistence, reloptions from pg_class c, pg_namespace n where c.relnamespace = n.oid and n.nspname = 'global_temporary_table' order by relkind,relpersistence,relname;
 
 reset search_path;
 drop schema global_temporary_table cascade;
-- 
2.17.1

---

From: Pavel Stehule <[email-masked]>
Date: Sun, 27 Feb 2022 04:17:52 +0100

Hi

> at pg_get_gtt_statistics.

Without this, the GTT will be terribly slow like current temporary tables
with a lot of problems with bloating of pg_class, pg_attribute and
pg_depend tables.

Regards

Pavel