v6-0008-Add-pg_temp_statistic-global-temporary-catalog-ta.patch
text/x-patch
Filename: v6-0008-Add-pg_temp_statistic-global-temporary-catalog-ta.patch
Type: text/x-patch
Part: 7
Message:
Re: Global temporary tables
Patch
Format: format-patch
Series: patch v6-0008
Subject: Add pg_temp_statistic global temporary catalog table.
| File | + | − |
|---|---|---|
| src/backend/catalog/heap.c | 20 | 2 |
| src/backend/catalog/system_views.sql | 8 | 1 |
| src/backend/commands/analyze.c | 30 | 14 |
| src/backend/executor/nodeHash.c | 2 | 1 |
| src/backend/statistics/attribute_stats.c | 27 | 4 |
| src/backend/utils/adt/selfuncs.c | 12 | 6 |
| src/backend/utils/cache/lsyscache.c | 9 | 3 |
| src/backend/utils/cache/relcache.c | 1 | 0 |
| src/include/catalog/Makefile | 2 | 1 |
| src/include/catalog/meson.build | 1 | 0 |
| src/include/catalog/pg_statistic.h | 3 | 0 |
| src/include/catalog/pg_temp_statistic.h | 147 | 0 |
| src/test/regress/expected/global_temp.out | 37 | 0 |
| src/test/regress/expected/oidjoins.out | 11 | 0 |
| src/test/regress/expected/rules.out | 66 | 1 |
| src/test/regress/expected/sanity_check.out | 23 | 0 |
| src/test/regress/sql/global_temp.sql | 19 | 0 |
| src/test/regress/sql/sanity_check.sql | 20 | 0 |
From 20064dd7ffc2f260386133cdaecc995f30d8088f Mon Sep 17 00:00:00 2001
From: Dean Rasheed <dean.a.rasheed@gmail.com>
Date: Thu, 18 Jun 2026 20:49:45 +0100
Subject: [PATCH v6 08/10] Add pg_temp_statistic global temporary catalog
table.
This has the exact same columns as pg_statistic, but it is a global
temporary table, and is used to hold statistics about other global
temporary relations, allowing them to be session-specific.
---
src/backend/catalog/heap.c | 22 ++-
src/backend/catalog/system_views.sql | 9 +-
src/backend/commands/analyze.c | 44 ++++--
src/backend/executor/nodeHash.c | 3 +-
src/backend/statistics/attribute_stats.c | 31 ++++-
src/backend/utils/adt/selfuncs.c | 18 ++-
src/backend/utils/cache/lsyscache.c | 12 +-
src/backend/utils/cache/relcache.c | 1 +
src/include/catalog/Makefile | 3 +-
src/include/catalog/meson.build | 1 +
src/include/catalog/pg_statistic.h | 3 +
src/include/catalog/pg_temp_statistic.h | 147 +++++++++++++++++++++
src/test/regress/expected/global_temp.out | 37 ++++++
src/test/regress/expected/oidjoins.out | 11 ++
src/test/regress/expected/rules.out | 67 +++++++++-
src/test/regress/expected/sanity_check.out | 23 ++++
src/test/regress/sql/global_temp.sql | 19 +++
src/test/regress/sql/sanity_check.sql | 20 +++
18 files changed, 438 insertions(+), 33 deletions(-)
create mode 100644 src/include/catalog/pg_temp_statistic.h
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 684e9482f15..6d61650621a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -53,6 +53,7 @@
#include "catalog/pg_statistic.h"
#include "catalog/pg_subscription_rel.h"
#include "catalog/pg_tablespace.h"
+#include "catalog/pg_temp_statistic.h"
#include "catalog/pg_type.h"
#include "catalog/storage.h"
#include "commands/tablecmds.h"
@@ -3497,6 +3498,13 @@ CopyStatistics(Oid fromrelid, Oid torelid)
Relation statrel;
CatalogIndexState indstate = NULL;
+ /*
+ * Note: This is currently only used for concurrent index building, which
+ * isn't supported on global temporary relations, so we never want
+ * pg_temp_statistic here.
+ */
+ Assert(!rel_is_global_temp(fromrelid) && !rel_is_global_temp(torelid));
+
statrel = table_open(StatisticRelationId, RowExclusiveLock);
/* Now search for stat records */
@@ -3545,12 +3553,22 @@ void
RemoveStatistics(Oid relid, AttrNumber attnum)
{
Relation pgstatistic;
+ Oid relidAttnumInhIndexId;
SysScanDesc scan;
ScanKeyData key[2];
int nkeys;
HeapTuple tuple;
- pgstatistic = table_open(StatisticRelationId, RowExclusiveLock);
+ if (rel_is_global_temp(relid))
+ {
+ pgstatistic = table_open(TempStatisticRelationId, RowExclusiveLock);
+ relidAttnumInhIndexId = TempStatisticRelidAttnumInhIndexId;
+ }
+ else
+ {
+ pgstatistic = table_open(StatisticRelationId, RowExclusiveLock);
+ relidAttnumInhIndexId = StatisticRelidAttnumInhIndexId;
+ }
ScanKeyInit(&key[0],
Anum_pg_statistic_starelid,
@@ -3568,7 +3586,7 @@ RemoveStatistics(Oid relid, AttrNumber attnum)
nkeys = 2;
}
- scan = systable_beginscan(pgstatistic, StatisticRelidAttnumInhIndexId, true,
+ scan = systable_beginscan(pgstatistic, relidAttnumInhIndexId, true,
NULL, nkeys, key);
/* we must loop even when attnum != 0, in case of inherited stats */
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 6c1c5545cb5..d2fe17d6a97 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -187,6 +187,11 @@ CREATE VIEW pg_sequences AS
WHERE NOT pg_is_other_temp_schema(N.oid)
AND relkind = 'S';
+CREATE VIEW pg_all_statistic AS
+ SELECT * FROM pg_statistic
+ UNION ALL
+ SELECT * FROM pg_temp_statistic;
+
CREATE VIEW pg_stats WITH (security_barrier) AS
SELECT
nspname AS schemaname,
@@ -268,7 +273,7 @@ CREATE VIEW pg_stats WITH (security_barrier) AS
WHEN stakind4 = 7 THEN stavalues4
WHEN stakind5 = 7 THEN stavalues5
END AS range_bounds_histogram
- FROM pg_statistic s JOIN pg_class c ON (c.oid = s.starelid)
+ FROM pg_all_statistic s JOIN pg_class c ON (c.oid = s.starelid)
JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum)
LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
WHERE NOT attisdropped
@@ -276,6 +281,8 @@ CREATE VIEW pg_stats WITH (security_barrier) AS
AND (c.relrowsecurity = false OR NOT row_security_active(c.oid));
REVOKE ALL ON pg_statistic FROM public;
+REVOKE ALL ON pg_temp_statistic FROM public;
+REVOKE ALL ON pg_all_statistic FROM public;
CREATE VIEW pg_stats_ext WITH (security_barrier) AS
SELECT cn.nspname AS schemaname,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index f66e80b757c..0db9810eba1 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -29,6 +29,7 @@
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_inherits.h"
+#include "catalog/pg_temp_statistic.h"
#include "commands/progress.h"
#include "commands/tablecmds.h"
#include "commands/vacuum.h"
@@ -176,9 +177,11 @@ analyze_rel(Oid relid, RangeVar *relation,
}
/*
- * We can ANALYZE any table except pg_statistic. See update_attstats
+ * We can ANALYZE any table except pg_statistic and pg_temp_statistic. See
+ * update_attstats
*/
- if (RelationGetRelid(onerel) == StatisticRelationId)
+ if (RelationGetRelid(onerel) == StatisticRelationId ||
+ RelationGetRelid(onerel) == TempStatisticRelationId)
{
relation_close(onerel, ShareUpdateExclusiveLock);
return;
@@ -1691,20 +1694,23 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
/*
* update_attstats() -- update attribute statistics for one relation
*
- * Statistics are stored in several places: the pg_class row for the
- * relation has stats about the whole relation, and there is a
- * pg_statistic row for each (non-system) attribute that has ever
- * been analyzed. The pg_class values are updated by VACUUM, not here.
+ * Statistics are stored in several places: the pg_class/pg_temp_class
+ * row for the relation has stats about the whole relation, and there is
+ * a pg_statistic/pg_temp_statistic row for each (non-system) attribute
+ * that has ever been analyzed. The pg_class/pg_temp_class values are
+ * updated by VACUUM, not here.
*
- * pg_statistic rows are just added or updated normally. This means
- * that pg_statistic will probably contain some deleted rows at the
- * completion of a vacuum cycle, unless it happens to get vacuumed last.
+ * pg_statistic/pg_temp_statistic rows are just added or updated
+ * normally. This means that pg_statistic/pg_temp_statistic will
+ * probably contain some deleted rows at the completion of a vacuum
+ * cycle, unless it happens to get vacuumed last.
*
- * To keep things simple, we punt for pg_statistic, and don't try
- * to compute or store rows for pg_statistic itself in pg_statistic.
+ * To keep things simple, we punt for pg_statistic and pg_temp_statistic,
+ * and don't try to compute or store rows for pg_statistic or
+ * pg_temp_statistic themselves in pg_statistic or pg_temp_statistic.
* This could possibly be made to work, but it's not worth the trouble.
* Note analyze_rel() has seen to it that we won't come here when
- * vacuuming pg_statistic itself.
+ * vacuuming pg_statistic or pg_temp_statistic themselves.
*
* Note: there would be a race condition here if two backends could
* ANALYZE the same table concurrently. Presently, we lock that out
@@ -1716,11 +1722,21 @@ update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats)
Relation sd;
int attno;
CatalogIndexState indstate = NULL;
+ SysCacheIdentifier cacheId;
if (natts <= 0)
return; /* nothing to do */
- sd = table_open(StatisticRelationId, RowExclusiveLock);
+ if (rel_is_global_temp(relid))
+ {
+ sd = table_open(TempStatisticRelationId, RowExclusiveLock);
+ cacheId = TEMPSTATRELATTINH;
+ }
+ else
+ {
+ sd = table_open(StatisticRelationId, RowExclusiveLock);
+ cacheId = STATRELATTINH;
+ }
for (attno = 0; attno < natts; attno++)
{
@@ -1811,7 +1827,7 @@ update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats)
}
/* Is there already a pg_statistic tuple for this attribute? */
- oldtup = SearchSysCache3(STATRELATTINH,
+ oldtup = SearchSysCache3(cacheId,
ObjectIdGetDatum(relid),
Int16GetDatum(stats->tupattnum),
BoolGetDatum(inh));
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 8825bb6fa23..6ace6904810 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -2442,7 +2442,8 @@ ExecHashBuildSkewHash(HashState *hashstate, HashJoinTable hashtable,
/*
* Try to find the MCV statistics for the outer relation's join key.
*/
- statsTuple = SearchSysCache3(STATRELATTINH,
+ statsTuple = SearchSysCache3(rel_is_global_temp(node->skewTable) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
ObjectIdGetDatum(node->skewTable),
Int16GetDatum(node->skewColumn),
BoolGetDatum(node->skewInherit));
diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c
index 1cc4d657231..60ffc7db04f 100644
--- a/src/backend/statistics/attribute_stats.c
+++ b/src/backend/statistics/attribute_stats.c
@@ -21,6 +21,7 @@
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_operator.h"
+#include "catalog/pg_temp_statistic.h"
#include "nodes/makefuncs.h"
#include "statistics/statistics.h"
#include "statistics/stat_utils.h"
@@ -137,6 +138,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
Oid locked_table = InvalidOid;
Relation starel;
+ SysCacheIdentifier cacheId;
HeapTuple statup;
Oid atttypid = InvalidOid;
@@ -332,9 +334,18 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
fmgr_info(F_ARRAY_IN, &array_in_fn);
- starel = table_open(StatisticRelationId, RowExclusiveLock);
+ if (rel_is_global_temp(reloid))
+ {
+ starel = table_open(TempStatisticRelationId, RowExclusiveLock);
+ cacheId = TEMPSTATRELATTINH;
+ }
+ else
+ {
+ starel = table_open(StatisticRelationId, RowExclusiveLock);
+ cacheId = STATRELATTINH;
+ }
- statup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(reloid), Int16GetDatum(attnum), BoolGetDatum(inherited));
+ statup = SearchSysCache3(cacheId, ObjectIdGetDatum(reloid), Int16GetDatum(attnum), BoolGetDatum(inherited));
/* initialize from existing tuple if exists */
if (HeapTupleIsValid(statup))
@@ -567,12 +578,24 @@ upsert_pg_statistic(Relation starel, HeapTuple oldtup,
static bool
delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit)
{
- Relation sd = table_open(StatisticRelationId, RowExclusiveLock);
+ Relation sd;
HeapTuple oldtup;
bool result = false;
+ SysCacheIdentifier cacheId;
+
+ if (rel_is_global_temp(reloid))
+ {
+ sd = table_open(TempStatisticRelationId, RowExclusiveLock);
+ cacheId = TEMPSTATRELATTINH;
+ }
+ else
+ {
+ sd = table_open(StatisticRelationId, RowExclusiveLock);
+ cacheId = STATRELATTINH;
+ }
/* Is there already a pg_statistic tuple for this attribute? */
- oldtup = SearchSysCache3(STATRELATTINH,
+ oldtup = SearchSysCache3(cacheId,
ObjectIdGetDatum(reloid),
Int16GetDatum(attnum),
BoolGetDatum(stainherit));
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index cbc70fde716..82be6a4d2af 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5838,7 +5838,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
else if (index->indpred == NIL)
{
vardata->statsTuple =
- SearchSysCache3(STATRELATTINH,
+ SearchSysCache3(rel_is_global_temp(index->indexoid) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
ObjectIdGetDatum(index->indexoid),
Int16GetDatum(pos + 1),
BoolGetDatum(false));
@@ -6068,7 +6069,8 @@ examine_simple_variable(PlannerInfo *root, Var *var,
* Plain table or parent of an inheritance appendrel, so look up the
* column in pg_statistic
*/
- vardata->statsTuple = SearchSysCache3(STATRELATTINH,
+ vardata->statsTuple = SearchSysCache3(rel_is_global_temp(rte->relid) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
ObjectIdGetDatum(rte->relid),
Int16GetDatum(var->varattno),
BoolGetDatum(rte->inh));
@@ -6545,7 +6547,8 @@ examine_indexcol_variable(PlannerInfo *root, IndexOptInfo *index,
}
else
{
- vardata->statsTuple = SearchSysCache3(STATRELATTINH,
+ vardata->statsTuple = SearchSysCache3(rel_is_global_temp(relid) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
ObjectIdGetDatum(relid),
Int16GetDatum(colnum),
BoolGetDatum(rte->inh));
@@ -6571,7 +6574,8 @@ examine_indexcol_variable(PlannerInfo *root, IndexOptInfo *index,
}
else
{
- vardata->statsTuple = SearchSysCache3(STATRELATTINH,
+ vardata->statsTuple = SearchSysCache3(rel_is_global_temp(relid) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
ObjectIdGetDatum(relid),
Int16GetDatum(colnum),
BoolGetDatum(false));
@@ -9125,7 +9129,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
else
{
vardata.statsTuple =
- SearchSysCache3(STATRELATTINH,
+ SearchSysCache3(rel_is_global_temp(rte->relid) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attnum),
BoolGetDatum(false));
@@ -9155,7 +9160,8 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
}
else
{
- vardata.statsTuple = SearchSysCache3(STATRELATTINH,
+ vardata.statsTuple = SearchSysCache3(rel_is_global_temp(index->indexoid) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
ObjectIdGetDatum(index->indexoid),
Int16GetDatum(attnum),
BoolGetDatum(false));
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index e8068a45cc8..ecc34f07e36 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -41,6 +41,7 @@
#include "catalog/pg_statistic.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_temp_class.h"
+#include "catalog/pg_temp_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -3499,7 +3500,8 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
if (stawidth > 0)
return stawidth;
}
- tp = SearchSysCache3(STATRELATTINH,
+ tp = SearchSysCache3(rel_is_global_temp(relid) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
ObjectIdGetDatum(relid),
Int16GetDatum(attnum),
BoolGetDatum(false));
@@ -3593,7 +3595,9 @@ get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
if (flags & ATTSTATSSLOT_VALUES)
{
- val = SysCacheGetAttrNotNull(STATRELATTINH, statstuple,
+ val = SysCacheGetAttrNotNull(IsTempStatisticTuple(statstuple) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
+ statstuple,
Anum_pg_statistic_stavalues1 + i);
/*
@@ -3638,7 +3642,9 @@ get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
if (flags & ATTSTATSSLOT_NUMBERS)
{
- val = SysCacheGetAttrNotNull(STATRELATTINH, statstuple,
+ val = SysCacheGetAttrNotNull(IsTempStatisticTuple(statstuple) ?
+ TEMPSTATRELATTINH : STATRELATTINH,
+ statstuple,
Anum_pg_statistic_stanumbers1 + i);
/*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index e857e36eb4a..b144d0639b8 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -63,6 +63,7 @@
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_temp_class.h"
+#include "catalog/pg_temp_statistic.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/schemapg.h"
diff --git a/src/include/catalog/Makefile b/src/include/catalog/Makefile
index 629a13edc24..6aec03add22 100644
--- a/src/include/catalog/Makefile
+++ b/src/include/catalog/Makefile
@@ -87,7 +87,8 @@ CATALOG_HEADERS := \
pg_propgraph_label.h \
pg_propgraph_label_property.h \
pg_propgraph_property.h \
- pg_temp_class.h
+ pg_temp_class.h \
+ pg_temp_statistic.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h)
diff --git a/src/include/catalog/meson.build b/src/include/catalog/meson.build
index 404f8503276..db809c4b3c0 100644
--- a/src/include/catalog/meson.build
+++ b/src/include/catalog/meson.build
@@ -75,6 +75,7 @@ catalog_headers = [
'pg_propgraph_label_property.h',
'pg_propgraph_property.h',
'pg_temp_class.h',
+ 'pg_temp_statistic.h',
]
# The .dat files we need can just be listed alphabetically.
diff --git a/src/include/catalog/pg_statistic.h b/src/include/catalog/pg_statistic.h
index 032bf177b95..ff55f7f5bb4 100644
--- a/src/include/catalog/pg_statistic.h
+++ b/src/include/catalog/pg_statistic.h
@@ -28,6 +28,9 @@
*/
BEGIN_CATALOG_STRUCT
+/*
+ * NB: Any changes made here must be reflected in pg_temp_statistic.
+ */
CATALOG(pg_statistic,2619,StatisticRelationId)
{
/* These fields form the unique key for the entry: */
diff --git a/src/include/catalog/pg_temp_statistic.h b/src/include/catalog/pg_temp_statistic.h
new file mode 100644
index 00000000000..afad9dd660c
--- /dev/null
+++ b/src/include/catalog/pg_temp_statistic.h
@@ -0,0 +1,147 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_temp_statistic.h
+ * definition of the "temporary statistics" system catalog
+ * (pg_temp_statistic)
+ *
+ * This is a global temporary system catalog table storing session-specific
+ * statistics for temporary relations. Currently, it is only used for
+ * global temporary relations.
+ *
+ * Portions Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * src/include/catalog/pg_temp_statistic.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_TEMP_STATISTIC_H
+#define PG_TEMP_STATISTIC_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_temp_statistic_d.h" /* IWYU pragma: export */
+
+/* ----------------
+ * pg_temp_statistic definition. cpp turns this into
+ * typedef struct FormData_pg_temp_statistic
+ * ----------------
+ */
+BEGIN_CATALOG_STRUCT
+
+/*
+ * NB: The fields here must exactly match those in pg_statistic.
+ */
+CATALOG(pg_temp_statistic,8084,TempStatisticRelationId) BKI_TEMP_RELATION
+{
+ /* These fields form the unique key for the entry: */
+ Oid starelid BKI_LOOKUP(pg_class); /* relation containing
+ * attribute */
+ int16 staattnum; /* attribute (column) stats are for */
+ bool stainherit; /* true if inheritance children are included */
+
+ /* the fraction of the column's entries that are NULL: */
+ float4 stanullfrac;
+
+ /*
+ * stawidth is the average width in bytes of non-null entries. For
+ * fixed-width datatypes this is of course the same as the typlen, but for
+ * var-width types it is more useful. Note that this is the average width
+ * of the data as actually stored, post-TOASTing (eg, for a
+ * moved-out-of-line value, only the size of the pointer object is
+ * counted). This is the appropriate definition for the primary use of
+ * the statistic, which is to estimate sizes of in-memory hash tables of
+ * tuples.
+ */
+ int32 stawidth;
+
+ /* ----------------
+ * stadistinct indicates the (approximate) number of distinct non-null
+ * data values in the column. The interpretation is:
+ * 0 unknown or not computed
+ * > 0 actual number of distinct values
+ * < 0 negative of multiplier for number of rows
+ * The special negative case allows us to cope with columns that are
+ * unique (stadistinct = -1) or nearly so (for example, a column in which
+ * non-null values appear about twice on the average could be represented
+ * by stadistinct = -0.5 if there are no nulls, or -0.4 if 20% of the
+ * column is nulls). Because the number-of-rows statistic in pg_class may
+ * be updated more frequently than pg_statistic is, it's important to be
+ * able to describe such situations as a multiple of the number of rows,
+ * rather than a fixed number of distinct values. But in other cases a
+ * fixed number is correct (eg, a boolean column).
+ * ----------------
+ */
+ float4 stadistinct;
+
+ /* ----------------
+ * To allow keeping statistics on different kinds of datatypes,
+ * we do not hard-wire any particular meaning for the remaining
+ * statistical fields. Instead, we provide several "slots" in which
+ * statistical data can be placed. Each slot includes:
+ * kind integer code identifying kind of data (see below)
+ * op OID of associated operator, if needed
+ * coll OID of relevant collation, or 0 if none
+ * numbers float4 array (for statistical values)
+ * values anyarray (for representations of data values)
+ * The ID, operator, and collation fields are never NULL; they are zeroes
+ * in an unused slot. The numbers and values fields are NULL in an
+ * unused slot, and might also be NULL in a used slot if the slot kind
+ * has no need for one or the other.
+ * ----------------
+ */
+
+ int16 stakind1;
+ int16 stakind2;
+ int16 stakind3;
+ int16 stakind4;
+ int16 stakind5;
+
+ Oid staop1 BKI_LOOKUP_OPT(pg_operator);
+ Oid staop2 BKI_LOOKUP_OPT(pg_operator);
+ Oid staop3 BKI_LOOKUP_OPT(pg_operator);
+ Oid staop4 BKI_LOOKUP_OPT(pg_operator);
+ Oid staop5 BKI_LOOKUP_OPT(pg_operator);
+
+ Oid stacoll1 BKI_LOOKUP_OPT(pg_collation);
+ Oid stacoll2 BKI_LOOKUP_OPT(pg_collation);
+ Oid stacoll3 BKI_LOOKUP_OPT(pg_collation);
+ Oid stacoll4 BKI_LOOKUP_OPT(pg_collation);
+ Oid stacoll5 BKI_LOOKUP_OPT(pg_collation);
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+ float4 stanumbers1[1];
+ float4 stanumbers2[1];
+ float4 stanumbers3[1];
+ float4 stanumbers4[1];
+ float4 stanumbers5[1];
+
+ /*
+ * Values in these arrays are values of the column's data type, or of some
+ * related type such as an array element type. We presently have to cheat
+ * quite a bit to allow polymorphic arrays of this kind, but perhaps
+ * someday it'll be a less bogus facility.
+ */
+ anyarray stavalues1;
+ anyarray stavalues2;
+ anyarray stavalues3;
+ anyarray stavalues4;
+ anyarray stavalues5;
+#endif
+} FormData_pg_temp_statistic;
+
+END_CATALOG_STRUCT
+
+DECLARE_TOAST(pg_temp_statistic, 8085, 8086);
+
+DECLARE_UNIQUE_INDEX_PKEY(pg_temp_statistic_relid_att_inh_index, 8087, TempStatisticRelidAttnumInhIndexId, pg_temp_statistic, btree(starelid oid_ops, staattnum int2_ops, stainherit bool_ops));
+
+MAKE_SYSCACHE(TEMPSTATRELATTINH, pg_temp_statistic_relid_att_inh_index, 128);
+
+/* Is the specified tuple from pg_temp_statistic? */
+#define IsTempStatisticTuple(tuple) \
+ ((tuple)->t_tableOid == TempStatisticRelationId)
+
+#endif /* PG_TEMP_STATISTIC_H */
diff --git a/src/test/regress/expected/global_temp.out b/src/test/regress/expected/global_temp.out
index 67fda4d2ffb..ca1ee3a485d 100644
--- a/src/test/regress/expected/global_temp.out
+++ b/src/test/regress/expected/global_temp.out
@@ -568,6 +568,43 @@ SELECT oid::regclass, relpages, reltuples, relallvisible, relallfrozen
tmp2 | 5 | 150 | 10 | 20
(1 row)
+DROP TABLE tmp2;
+-- Test column stats
+CREATE GLOBAL TEMP TABLE tmp2 (a int, b int);
+INSERT INTO tmp2 SELECT x, floor(sqrt(x)) FROM generate_series(36, 99) x;
+ANALYZE tmp2;
+SELECT stavalues1 FROM pg_statistic
+ WHERE starelid = 'tmp2'::regclass AND staattnum = 2;
+ stavalues1
+------------
+(0 rows)
+
+SELECT stavalues1 FROM pg_temp_statistic
+ WHERE starelid = 'tmp2'::regclass AND staattnum = 2;
+ stavalues1
+------------
+ {9,8,7,6}
+(1 row)
+
+SELECT most_common_vals FROM pg_stats
+ WHERE tablename = 'tmp2' AND attname = 'b';
+ most_common_vals
+------------------
+ {9,8,7,6}
+(1 row)
+
+SELECT COUNT(*) FROM tmp2 WHERE b = 8;
+ count
+-------
+ 17
+(1 row)
+
+SELECT row_estimate('SELECT * FROM tmp2 WHERE b = 8');
+ row_estimate
+--------------
+ 17
+(1 row)
+
DROP TABLE tmp2;
-- Test view creation
INSERT INTO tmp1 VALUES (1, 'xxx');
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 3c3404e51b8..be2efa78257 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -287,3 +287,14 @@ NOTICE: checking pg_propgraph_property {pgptypid} => pg_type {oid}
NOTICE: checking pg_propgraph_property {pgpcollation} => pg_collation {oid}
NOTICE: checking pg_temp_class {oid} => pg_class {oid}
NOTICE: checking pg_temp_class {reltablespace} => pg_tablespace {oid}
+NOTICE: checking pg_temp_statistic {starelid} => pg_class {oid}
+NOTICE: checking pg_temp_statistic {staop1} => pg_operator {oid}
+NOTICE: checking pg_temp_statistic {staop2} => pg_operator {oid}
+NOTICE: checking pg_temp_statistic {staop3} => pg_operator {oid}
+NOTICE: checking pg_temp_statistic {staop4} => pg_operator {oid}
+NOTICE: checking pg_temp_statistic {staop5} => pg_operator {oid}
+NOTICE: checking pg_temp_statistic {stacoll1} => pg_collation {oid}
+NOTICE: checking pg_temp_statistic {stacoll2} => pg_collation {oid}
+NOTICE: checking pg_temp_statistic {stacoll3} => pg_collation {oid}
+NOTICE: checking pg_temp_statistic {stacoll4} => pg_collation {oid}
+NOTICE: checking pg_temp_statistic {stacoll5} => pg_collation {oid}
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e266f501d87..f8031bdd14e 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1302,6 +1302,71 @@ pg_aios| SELECT pid,
f_localmem,
f_buffered
FROM pg_get_aios() pg_get_aios(pid, io_id, io_generation, state, operation, off, length, target, handle_data_len, raw_result, result, target_desc, f_sync, f_localmem, f_buffered);
+pg_all_statistic| SELECT pg_statistic.starelid,
+ pg_statistic.staattnum,
+ pg_statistic.stainherit,
+ pg_statistic.stanullfrac,
+ pg_statistic.stawidth,
+ pg_statistic.stadistinct,
+ pg_statistic.stakind1,
+ pg_statistic.stakind2,
+ pg_statistic.stakind3,
+ pg_statistic.stakind4,
+ pg_statistic.stakind5,
+ pg_statistic.staop1,
+ pg_statistic.staop2,
+ pg_statistic.staop3,
+ pg_statistic.staop4,
+ pg_statistic.staop5,
+ pg_statistic.stacoll1,
+ pg_statistic.stacoll2,
+ pg_statistic.stacoll3,
+ pg_statistic.stacoll4,
+ pg_statistic.stacoll5,
+ pg_statistic.stanumbers1,
+ pg_statistic.stanumbers2,
+ pg_statistic.stanumbers3,
+ pg_statistic.stanumbers4,
+ pg_statistic.stanumbers5,
+ pg_statistic.stavalues1,
+ pg_statistic.stavalues2,
+ pg_statistic.stavalues3,
+ pg_statistic.stavalues4,
+ pg_statistic.stavalues5
+ FROM pg_statistic
+UNION ALL
+ SELECT pg_temp_statistic.starelid,
+ pg_temp_statistic.staattnum,
+ pg_temp_statistic.stainherit,
+ pg_temp_statistic.stanullfrac,
+ pg_temp_statistic.stawidth,
+ pg_temp_statistic.stadistinct,
+ pg_temp_statistic.stakind1,
+ pg_temp_statistic.stakind2,
+ pg_temp_statistic.stakind3,
+ pg_temp_statistic.stakind4,
+ pg_temp_statistic.stakind5,
+ pg_temp_statistic.staop1,
+ pg_temp_statistic.staop2,
+ pg_temp_statistic.staop3,
+ pg_temp_statistic.staop4,
+ pg_temp_statistic.staop5,
+ pg_temp_statistic.stacoll1,
+ pg_temp_statistic.stacoll2,
+ pg_temp_statistic.stacoll3,
+ pg_temp_statistic.stacoll4,
+ pg_temp_statistic.stacoll5,
+ pg_temp_statistic.stanumbers1,
+ pg_temp_statistic.stanumbers2,
+ pg_temp_statistic.stanumbers3,
+ pg_temp_statistic.stanumbers4,
+ pg_temp_statistic.stanumbers5,
+ pg_temp_statistic.stavalues1,
+ pg_temp_statistic.stavalues2,
+ pg_temp_statistic.stavalues3,
+ pg_temp_statistic.stavalues4,
+ pg_temp_statistic.stavalues5
+ FROM pg_temp_statistic;
pg_available_extension_versions| SELECT e.name,
e.version,
(x.extname IS NOT NULL) AS installed,
@@ -2700,7 +2765,7 @@ pg_stats| SELECT n.nspname AS schemaname,
WHEN (s.stakind5 = 7) THEN s.stavalues5
ELSE NULL::anyarray
END AS range_bounds_histogram
- FROM (((pg_statistic s
+ FROM (((pg_all_statistic s
JOIN pg_class c ON ((c.oid = s.starelid)))
JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum))))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 8370c1561cc..5f5756d5596 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -25,3 +25,26 @@ SELECT relname, relkind
---------+---------
(0 rows)
+-- check that pg_statistic and pg_temp_statistic have the exact same columns
+WITH t1 AS (
+ SELECT attname, atttypid, attlen, attnum, atttypmod, attndims, attbyval,
+ attalign, attstorage, attcompression, attnotnull, atthasdef,
+ atthasmissing, attidentity, attgenerated, attisdropped, attislocal,
+ attinhcount, attcollation
+ FROM pg_attribute
+ WHERE attrelid = 'pg_statistic'::regclass
+), t2 AS (
+ SELECT attname, atttypid, attlen, attnum, atttypmod, attndims, attbyval,
+ attalign, attstorage, attcompression, attnotnull, atthasdef,
+ atthasmissing, attidentity, attgenerated, attisdropped, attislocal,
+ attinhcount, attcollation
+ FROM pg_attribute
+ WHERE attrelid = 'pg_temp_statistic'::regclass
+)
+(SELECT * FROM t1 EXCEPT SELECT * FROM t2)
+UNION ALL
+(SELECT * FROM t2 EXCEPT SELECT * FROM t1);
+ attname | atttypid | attlen | attnum | atttypmod | attndims | attbyval | attalign | attstorage | attcompression | attnotnull | atthasdef | atthasmissing | attidentity | attgenerated | attisdropped | attislocal | attinhcount | attcollation
+---------+----------+--------+--------+-----------+----------+----------+----------+------------+----------------+------------+-----------+---------------+-------------+--------------+--------------+------------+-------------+--------------
+(0 rows)
+
diff --git a/src/test/regress/sql/global_temp.sql b/src/test/regress/sql/global_temp.sql
index ed262910aff..c08ad074e71 100644
--- a/src/test/regress/sql/global_temp.sql
+++ b/src/test/regress/sql/global_temp.sql
@@ -337,6 +337,25 @@ SELECT oid::regclass, relpages, reltuples, relallvisible, relallfrozen
DROP TABLE tmp2;
+-- Test column stats
+CREATE GLOBAL TEMP TABLE tmp2 (a int, b int);
+INSERT INTO tmp2 SELECT x, floor(sqrt(x)) FROM generate_series(36, 99) x;
+ANALYZE tmp2;
+
+SELECT stavalues1 FROM pg_statistic
+ WHERE starelid = 'tmp2'::regclass AND staattnum = 2;
+
+SELECT stavalues1 FROM pg_temp_statistic
+ WHERE starelid = 'tmp2'::regclass AND staattnum = 2;
+
+SELECT most_common_vals FROM pg_stats
+ WHERE tablename = 'tmp2' AND attname = 'b';
+
+SELECT COUNT(*) FROM tmp2 WHERE b = 8;
+SELECT row_estimate('SELECT * FROM tmp2 WHERE b = 8');
+
+DROP TABLE tmp2;
+
-- Test view creation
INSERT INTO tmp1 VALUES (1, 'xxx');
CREATE VIEW v AS SELECT * FROM tmp1;
diff --git a/src/test/regress/sql/sanity_check.sql b/src/test/regress/sql/sanity_check.sql
index 162e5324b5d..ed0096d7823 100644
--- a/src/test/regress/sql/sanity_check.sql
+++ b/src/test/regress/sql/sanity_check.sql
@@ -19,3 +19,23 @@ SELECT relname, relkind
FROM pg_class
WHERE relkind IN ('v', 'c', 'f', 'p', 'I')
AND relfilenode <> 0;
+
+-- check that pg_statistic and pg_temp_statistic have the exact same columns
+WITH t1 AS (
+ SELECT attname, atttypid, attlen, attnum, atttypmod, attndims, attbyval,
+ attalign, attstorage, attcompression, attnotnull, atthasdef,
+ atthasmissing, attidentity, attgenerated, attisdropped, attislocal,
+ attinhcount, attcollation
+ FROM pg_attribute
+ WHERE attrelid = 'pg_statistic'::regclass
+), t2 AS (
+ SELECT attname, atttypid, attlen, attnum, atttypmod, attndims, attbyval,
+ attalign, attstorage, attcompression, attnotnull, atthasdef,
+ atthasmissing, attidentity, attgenerated, attisdropped, attislocal,
+ attinhcount, attcollation
+ FROM pg_attribute
+ WHERE attrelid = 'pg_temp_statistic'::regclass
+)
+(SELECT * FROM t1 EXCEPT SELECT * FROM t2)
+UNION ALL
+(SELECT * FROM t2 EXCEPT SELECT * FROM t1);
--
2.51.0