On Thu, Sep 03, 2026 at 08:21:49AM +0900, Michael Paquier wrote:
> So, isnull=true is not something that we can enforce using a C
> initializer of the type {0}.  However, it is something that we can
> enforce with an initializer macro, as in the lines of (same for atts
> and rels) in statistics.h: 
> #define RELATION_STATS_VALUES_NULL \
> { \
>    .version = {(Datum) 0, true}, \
>    .relpages = {(Datum) 0, true}, \
>    [...]
> }
> 
> And then use this initializer for the defined structures, for both
> the restore *and* the import code.

In short just the attached, minus the fields that do not need to be
set anymore.
--
Michael
From 2bf0323d78c9b214ecbec57ff79d86c08abb5b0f Mon Sep 17 00:00:00 2001
From: Michael Paquier <[email protected]>
Date: Thu, 3 Sep 2026 09:30:00 +0900
Subject: [PATCH] Add safer initializers for relation and attribute stats

These are used as a safer initialization measure should any of the
fields not be set, on a catalog-basis.  This is used in both the import
and the restore stats code.
---
 src/include/statistics/statistics.h      | 39 ++++++++++++++++++++++--
 src/backend/statistics/attribute_stats.c |  4 +--
 src/backend/statistics/relation_stats.c  |  4 +--
 contrib/postgres_fdw/postgres_fdw.c      |  8 ++---
 4 files changed, 41 insertions(+), 14 deletions(-)

diff --git a/src/include/statistics/statistics.h 
b/src/include/statistics/statistics.h
index fa0430b94774..71ebca40438d 100644
--- a/src/include/statistics/statistics.h
+++ b/src/include/statistics/statistics.h
@@ -132,7 +132,8 @@ extern HeapTuple statext_expressions_load(Oid stxoid, bool 
inh, int idx);
  * Statistics values applied to pg_class during stats import or restore
  *
  * A field with isnull set to true leaves the corresponding pg_class column
- * untouched.  The caller must initialize every field.
+ * untouched.  The caller must initialize every field; see also
+ * RELATION_STATS_VALUES_NULL.
  *
  * The "version" field is currently ignored.  In the future it can be used to
  * interpret the format of older statistics.
@@ -146,11 +147,24 @@ typedef struct RelationStatsValues
        NullableDatum relallfrozen;
 } RelationStatsValues;
 
+/*
+ * Initializer for a RelationStatsValues.
+ */
+#define RELATION_STATS_VALUES_NULL \
+{ \
+       .version = {.value = (Datum) 0, .isnull = true}, \
+       .relpages = {.value = (Datum) 0, .isnull = true}, \
+       .reltuples = {.value = (Datum) 0, .isnull = true}, \
+       .relallvisible = {.value = (Datum) 0, .isnull = true}, \
+       .relallfrozen = {.value = (Datum) 0, .isnull = true}, \
+}
+
 /*
  * Statistics values applied to pg_statistic during stats import or restore.
  *
  * A field with isnull set to true leaves the corresponding statistics kind
- * unset.  The caller must initialize every field.
+ * unset.  The caller must initialize every field; see also
+ * ATTRIBUTE_STATS_VALUES_NULL.
  *
  * The "version" field is currently ignored.  In the future, it can be used to
  * interpret the format of older statistics.
@@ -173,6 +187,27 @@ typedef struct AttributeStatsValues
        NullableDatum range_bounds_histogram;
 } AttributeStatsValues;
 
+/*
+ * Initializer for an AttributeStatsValues.
+ */
+#define ATTRIBUTE_STATS_VALUES_NULL \
+{ \
+       .version = {.value = (Datum) 0, .isnull = true}, \
+       .null_frac = {.value = (Datum) 0, .isnull = true}, \
+       .avg_width = {.value = (Datum) 0, .isnull = true}, \
+       .n_distinct = {.value = (Datum) 0, .isnull = true}, \
+       .most_common_vals = {.value = (Datum) 0, .isnull = true}, \
+       .most_common_freqs = {.value = (Datum) 0, .isnull = true}, \
+       .histogram_bounds = {.value = (Datum) 0, .isnull = true}, \
+       .correlation = {.value = (Datum) 0, .isnull = true}, \
+       .most_common_elems = {.value = (Datum) 0, .isnull = true}, \
+       .most_common_elem_freqs = {.value = (Datum) 0, .isnull = true}, \
+       .elem_count_histogram = {.value = (Datum) 0, .isnull = true}, \
+       .range_length_histogram = {.value = (Datum) 0, .isnull = true}, \
+       .range_empty_frac = {.value = (Datum) 0, .isnull = true}, \
+       .range_bounds_histogram = {.value = (Datum) 0, .isnull = true}, \
+}
+
 extern bool import_relation_statistics(Relation rel,
                                                                           
const RelationStatsValues *statvalues);
 extern bool import_attribute_statistics(Relation rel,
diff --git a/src/backend/statistics/attribute_stats.c 
b/src/backend/statistics/attribute_stats.c
index 1d19827dc45e..cd9794c952e6 100644
--- a/src/backend/statistics/attribute_stats.c
+++ b/src/backend/statistics/attribute_stats.c
@@ -140,7 +140,7 @@ attribute_statistics_update(const NullableDatum *args)
        AttrNumber      attnum;
        bool            inherited;
        Oid                     locked_table = InvalidOid;
-       AttributeStatsValues values;
+       AttributeStatsValues values = ATTRIBUTE_STATS_VALUES_NULL;
 
        stats_check_required_arg(args, attarginfo, ATTRELSCHEMA_ARG);
        stats_check_required_arg(args, attarginfo, ATTRELNAME_ARG);
@@ -206,8 +206,6 @@ attribute_statistics_update(const NullableDatum *args)
        inherited = DatumGetBool(args[INHERITED_ARG].value);
 
        /* Collect the values to apply */
-       values.version.value = (Datum) 0;
-       values.version.isnull = true;
        values.null_frac = args[NULL_FRAC_ARG];
        values.avg_width = args[AVG_WIDTH_ARG];
        values.n_distinct = args[N_DISTINCT_ARG];
diff --git a/src/backend/statistics/relation_stats.c 
b/src/backend/statistics/relation_stats.c
index 28d87fa6f77d..6e073c9cc0a2 100644
--- a/src/backend/statistics/relation_stats.c
+++ b/src/backend/statistics/relation_stats.c
@@ -73,7 +73,7 @@ relation_statistics_update(const NullableDatum *args)
        char       *relname;
        Oid                     reloid;
        Oid                     locked_table = InvalidOid;
-       RelationStatsValues values;
+       RelationStatsValues values = RELATION_STATS_VALUES_NULL;
 
        stats_check_required_arg(args, relarginfo, RELSCHEMA_ARG);
        stats_check_required_arg(args, relarginfo, RELNAME_ARG);
@@ -92,8 +92,6 @@ relation_statistics_update(const NullableDatum *args)
                                                                          
RangeVarCallbackForStats, &locked_table);
 
        /* Collect the values to apply. */
-       values.version.value = (Datum) 0;
-       values.version.isnull = true;
        values.relpages = args[RELPAGES_ARG];
        values.reltuples = args[RELTUPLES_ARG];
        values.relallvisible = args[RELALLVISIBLE_ARG];
diff --git a/contrib/postgres_fdw/postgres_fdw.c 
b/contrib/postgres_fdw/postgres_fdw.c
index 2cc594aecb69..97f45062e56c 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -6254,7 +6254,7 @@ import_fetched_statistics(Relation relation,
 {
        PGresult   *res;
        NullableDatum version;
-       RelationStatsValues relvalues;
+       RelationStatsValues relvalues = RELATION_STATS_VALUES_NULL;
 
        /* Set the 'version' value, which is common to both statistics. */
        version.value = Int32GetDatum(remstats->version);
@@ -6275,7 +6275,7 @@ import_fetched_statistics(Relation relation,
                {
                        int                     row = 
remattrmap[mapidx].res_index;
                        AttrNumber      attnum = 
remattrmap[mapidx].local_attnum;
-                       AttributeStatsValues attvalues;
+                       AttributeStatsValues attvalues = 
ATTRIBUTE_STATS_VALUES_NULL;
 
                        /* All mappings should have been assigned a result set 
row. */
                        Assert(row >= 0);
@@ -6345,10 +6345,6 @@ import_fetched_statistics(Relation relation,
                                  get_opt_value(res, 0, RELSTATS_RELTUPLES));
        Assert(!relvalues.reltuples.isnull);
        /* We don't import relallvisible/relallfrozen. */
-       relvalues.relallvisible.value = (Datum) 0;
-       relvalues.relallvisible.isnull = true;
-       relvalues.relallfrozen.value = (Datum) 0;
-       relvalues.relallfrozen.isnull = true;
 
        /* Try to import the statistics. */
        if (!import_relation_statistics(relation, &relvalues))
-- 
2.55.0

Attachment: signature.asc
Description: PGP signature

Reply via email to