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
signature.asc
Description: PGP signature
