v2-0010-Add-relation_statistics_update-refactor-update_re.patch
application/octet-stream
Filename: v2-0010-Add-relation_statistics_update-refactor-update_re.patch
Type: application/octet-stream
Part: 8
Patch
Format: format-patch
Series: patch v2-0010
Subject: Add relation_statistics_update, refactor update_relstats.
| File | + | − |
|---|---|---|
| src/backend/statistics/relation_stats.c | 193 | 51 |
| src/include/statistics/relation_stats.h | 30 | 0 |
From 34cc6ac8697c368b23583c18e9172747218b8b02 Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Sun, 28 Jun 2026 21:51:56 -0500
Subject: [PATCH v2 10/13] Add relation_statistics_update, refactor
update_relstats.
Introduce a new enum relation_stats_argnum, which is a subset of
relation_args_argnum but contains only the statistical values.
Modify update_relstats to take a Relation argument, and index the
NullableDatum array by the new enum relation_stats_argnum. All
processing and validation of non-statistical values like schema and
relname, as well as checking for recovery mode and acquiring the lock on
the relation are now handled in the calling functions
pg_restore_relation_stats and pg_clear_relation_stats. In turn, those
functions must now pass the shorter, statistics-only array of
NullableDatums, as well as the Relation argument that is now their
responsibility to open and close.
Create a new function relation_statistics_update which takes a Relation
argument and arrays of isnull and cstring arguments, which will be
translated into the Datum values required by update_relstats().
The end result is that all three user-facing functions are able to call
the same update_relstats().
---
src/backend/statistics/relation_stats.c | 244 +++++++++++++++++++-----
src/include/statistics/relation_stats.h | 30 +++
2 files changed, 223 insertions(+), 51 deletions(-)
create mode 100644 src/include/statistics/relation_stats.h
diff --git a/src/backend/statistics/relation_stats.c b/src/backend/statistics/relation_stats.c
index d4459981ebc..21baa0abb55 100644
--- a/src/backend/statistics/relation_stats.c
+++ b/src/backend/statistics/relation_stats.c
@@ -21,6 +21,7 @@
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "nodes/makefuncs.h"
+#include "statistics/relation_stats.h"
#include "statistics/stat_utils.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -56,18 +57,15 @@ static struct StatsArgInfo relarginfo[] =
[RELARG_NUM_RELARGS] = {0}
};
-static bool update_relstats(const NullableDatum *args);
+static bool update_relstats(Relation rel, const NullableDatum *args);
/*
* Internal function for modifying statistics for a relation.
*/
static bool
-update_relstats(const NullableDatum *args)
+update_relstats(Relation rel, const NullableDatum *args)
{
bool result = true;
- char *nspname;
- char *relname;
- Oid reloid;
Relation crel;
BlockNumber relpages = 0;
bool update_relpages = false;
@@ -83,33 +81,16 @@ update_relstats(const NullableDatum *args)
Datum values[4] = {0};
bool nulls[4] = {0};
int nreplaces = 0;
- Oid locked_table = InvalidOid;
-
- stats_check_required_arg(args, relarginfo, RELARG_SCHEMA);
- stats_check_required_arg(args, relarginfo, RELARG_RELNAME);
-
- nspname = TextDatumGetCString(args[RELARG_SCHEMA].value);
- relname = TextDatumGetCString(args[RELARG_RELNAME].value);
-
- if (RecoveryInProgress())
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("recovery is in progress"),
- errhint("Statistics cannot be modified during recovery.")));
-
- reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
- ShareUpdateExclusiveLock, 0,
- RangeVarCallbackForStats, &locked_table);
- if (!args[RELARG_RELPAGES].isnull)
+ if (!args[RELSTAT_RELPAGES].isnull)
{
- relpages = DatumGetUInt32(args[RELARG_RELPAGES].value);
+ relpages = DatumGetUInt32(args[RELSTAT_RELPAGES].value);
update_relpages = true;
}
- if (!args[RELARG_RELTUPLES].isnull)
+ if (!args[RELSTAT_RELTUPLES].isnull)
{
- reltuples = DatumGetFloat4(args[RELARG_RELTUPLES].value);
+ reltuples = DatumGetFloat4(args[RELSTAT_RELTUPLES].value);
if (reltuples < -1.0)
{
ereport(WARNING,
@@ -121,15 +102,15 @@ update_relstats(const NullableDatum *args)
update_reltuples = true;
}
- if (!args[RELARG_RELALLVISIBLE].isnull)
+ if (!args[RELSTAT_RELALLVISIBLE].isnull)
{
- relallvisible = DatumGetUInt32(args[RELARG_RELALLVISIBLE].value);
+ relallvisible = DatumGetUInt32(args[RELSTAT_RELALLVISIBLE].value);
update_relallvisible = true;
}
- if (!args[RELARG_RELALLFROZEN].isnull)
+ if (!args[RELSTAT_RELALLFROZEN].isnull)
{
- relallfrozen = DatumGetUInt32(args[RELARG_RELALLFROZEN].value);
+ relallfrozen = DatumGetUInt32(args[RELSTAT_RELALLFROZEN].value);
update_relallfrozen = true;
}
@@ -139,9 +120,9 @@ update_relstats(const NullableDatum *args)
*/
crel = table_open(RelationRelationId, RowExclusiveLock);
- ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid));
+ ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(RelationGetRelid(rel)));
if (!HeapTupleIsValid(ctup))
- elog(ERROR, "pg_class entry for relid %u not found", reloid);
+ elog(ERROR, "pg_class entry for relid %u not found", RelationGetRelid(rel));
pgcform = (Form_pg_class) GETSTRUCT(ctup);
@@ -201,24 +182,55 @@ update_relstats(const NullableDatum *args)
Datum
pg_clear_relation_stats(PG_FUNCTION_ARGS)
{
- LOCAL_FCINFO(newfcinfo, 6);
-
- InitFunctionCallInfoData(*newfcinfo, NULL, 6, InvalidOid, NULL, NULL);
-
- newfcinfo->args[0].value = PG_GETARG_DATUM(0);
- newfcinfo->args[0].isnull = PG_ARGISNULL(0);
- newfcinfo->args[1].value = PG_GETARG_DATUM(1);
- newfcinfo->args[1].isnull = PG_ARGISNULL(1);
- newfcinfo->args[2].value = UInt32GetDatum(0);
- newfcinfo->args[2].isnull = false;
- newfcinfo->args[3].value = Float4GetDatum(-1.0);
- newfcinfo->args[3].isnull = false;
- newfcinfo->args[4].value = UInt32GetDatum(0);
- newfcinfo->args[4].isnull = false;
- newfcinfo->args[5].value = UInt32GetDatum(0);
- newfcinfo->args[5].isnull = false;
-
- update_relstats(newfcinfo->args);
+ NullableDatum positional_args[RELARG_NUM_RELARGS];
+ NullableDatum stats[RELSTAT_NUM_RELSTATS];
+ char *nspname;
+ char *relname;
+ Oid reloid;
+ Oid locked_table = InvalidOid;
+ Relation rel;
+
+ /*
+ * Fill out just enough of positional_args to do the same required checks
+ * as pg_restore_relation_stats
+ */
+ positional_args[RELARG_SCHEMA].isnull = PG_ARGISNULL(0);
+ positional_args[RELARG_SCHEMA].value = PG_GETARG_DATUM(0);
+ positional_args[RELARG_RELNAME].isnull = PG_ARGISNULL(1);
+ positional_args[RELARG_RELNAME].value = PG_GETARG_DATUM(1);
+
+ stats_check_required_arg(positional_args, relarginfo, RELARG_SCHEMA);
+ stats_check_required_arg(positional_args, relarginfo, RELARG_RELNAME);
+
+ nspname = TextDatumGetCString(positional_args[RELARG_SCHEMA].value);
+ relname = TextDatumGetCString(positional_args[RELARG_RELNAME].value);
+
+ if (RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is in progress"),
+ errhint("Statistics cannot be modified during recovery.")));
+
+ reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
+ ShareUpdateExclusiveLock, 0,
+ RangeVarCallbackForStats, &locked_table);
+
+ stats[RELSTAT_RELPAGES].isnull = false;
+ stats[RELSTAT_RELPAGES].value = UInt32GetDatum(0);
+
+ stats[RELSTAT_RELTUPLES].isnull = false;
+ stats[RELSTAT_RELTUPLES].value = Float4GetDatum(-1.0);
+
+ stats[RELSTAT_RELALLVISIBLE].isnull = false;
+ stats[RELSTAT_RELALLVISIBLE].value = UInt32GetDatum(0);
+
+ stats[RELSTAT_RELALLFROZEN].isnull = false;
+ stats[RELSTAT_RELALLFROZEN].value = UInt32GetDatum(0);
+
+ rel = relation_open(reloid, NoLock);
+ update_relstats(rel, stats);
+ relation_close(rel, NoLock);
+
PG_RETURN_VOID();
}
@@ -226,14 +238,144 @@ Datum
pg_restore_relation_stats(PG_FUNCTION_ARGS)
{
NullableDatum positional_args[RELARG_NUM_RELARGS];
+ NullableDatum stats[RELSTAT_NUM_RELSTATS];
+
+ char *nspname;
+ char *relname;
+ Oid reloid;
+ Oid locked_table = InvalidOid;
+ Relation rel;
bool result = true;
if (!stats_fill_args_from_arg_pairs(fcinfo, positional_args,
relarginfo))
result = false;
- if (!update_relstats(positional_args))
+ stats_check_required_arg(positional_args, relarginfo, RELARG_SCHEMA);
+ stats_check_required_arg(positional_args, relarginfo, RELARG_RELNAME);
+
+ nspname = TextDatumGetCString(positional_args[RELARG_SCHEMA].value);
+ relname = TextDatumGetCString(positional_args[RELARG_RELNAME].value);
+
+ if (RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is in progress"),
+ errhint("Statistics cannot be modified during recovery.")));
+
+ reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
+ ShareUpdateExclusiveLock, 0,
+ RangeVarCallbackForStats, &locked_table);
+
+ /* Map RELARGs to RELSTATs */
+ stats[RELSTAT_RELPAGES].isnull = positional_args[RELARG_RELPAGES].isnull;
+ stats[RELSTAT_RELPAGES].value = positional_args[RELARG_RELPAGES].value;
+
+ stats[RELSTAT_RELTUPLES].isnull = positional_args[RELARG_RELTUPLES].isnull;
+ stats[RELSTAT_RELTUPLES].value = positional_args[RELARG_RELTUPLES].value;
+
+ stats[RELSTAT_RELALLVISIBLE].isnull = positional_args[RELARG_RELALLVISIBLE].isnull;
+ stats[RELSTAT_RELALLVISIBLE].value = positional_args[RELARG_RELALLVISIBLE].value;
+
+ stats[RELSTAT_RELALLFROZEN].isnull = positional_args[RELARG_RELALLFROZEN].isnull;
+ stats[RELSTAT_RELALLFROZEN].value = positional_args[RELARG_RELALLFROZEN].value;
+
+ rel = relation_open(reloid, NoLock);
+
+ if (!update_relstats(rel, stats))
result = false;
+ relation_close(rel, NoLock);
+
PG_RETURN_BOOL(result);
}
+
+/*
+ * Convenience routine to parse BlockNumber values, and emit a warning
+ * on parse errors.
+ */
+static void
+str_to_blocknumber(NullableDatum *stats, int statnum, const bool *isnull,
+ const char **values)
+{
+ stats[statnum].isnull = true;
+ stats[statnum].value = 0;
+
+ if (!isnull[statnum])
+ {
+ const char *s = values[statnum];
+ BlockNumber result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ if (s == NULL)
+ elog(ERROR, "value is null but flag is non-null");
+
+ result = uint32in_subr(s, NULL, "BlockNumber", (Node *) &escontext);
+
+ if (escontext.error_occurred)
+ {
+ escontext.error_data->elevel = WARNING;
+ ThrowErrorData(escontext.error_data);
+ FreeErrorData(escontext.error_data);
+ return;
+ }
+ else
+ {
+ stats[statnum].isnull = false;
+ stats[statnum].value = UInt32GetDatum(result);
+ }
+ }
+}
+
+/*
+ * Convenience routine to parse float values, and emit a warning on parse
+ * errors.
+ */
+static void
+str_to_float(NullableDatum *stats, int statnum, const bool *isnull,
+ const char **values)
+{
+ stats[statnum].isnull = true;
+ stats[statnum].value = 0;
+
+ if (!isnull[statnum])
+ {
+ const char *s = values[statnum];
+ Datum value;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ if (s == NULL)
+ elog(ERROR, "value is null but flag is non-null");
+
+ if (DirectInputFunctionCallSafe(float4in, (char *) s, InvalidOid, -1,
+ (Node *) &escontext, &value))
+ {
+ stats[statnum].isnull = false;
+ stats[statnum].value = value;
+ }
+ else
+ {
+ escontext.error_data->elevel = WARNING;
+ ThrowErrorData(escontext.error_data);
+ FreeErrorData(escontext.error_data);
+ return;
+ }
+ }
+}
+
+/*
+ * Update statistics for an already opened Relation with a lock level of at least
+ * ShareUpdateExclusiveLock.
+ */
+bool
+relation_statistics_update(Relation rel, const bool *isnull, const char **values)
+{
+ NullableDatum stats[RELSTAT_NUM_RELSTATS];
+
+ str_to_blocknumber(stats, RELSTAT_RELPAGES, isnull, values);
+ str_to_float(stats, RELSTAT_RELTUPLES, isnull, values);
+ str_to_blocknumber(stats, RELSTAT_RELALLVISIBLE, isnull, values);
+ str_to_blocknumber(stats, RELSTAT_RELALLFROZEN, isnull, values);
+
+ return update_relstats(rel, stats);
+}
diff --git a/src/include/statistics/relation_stats.h b/src/include/statistics/relation_stats.h
new file mode 100644
index 00000000000..56ce7ef3e66
--- /dev/null
+++ b/src/include/statistics/relation_stats.h
@@ -0,0 +1,30 @@
+/*-------------------------------------------------------------------------
+ *
+ * relation_stats.h
+ * Functions for the internal manipulation of relation statistics.
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/statistics/relation_stats.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RELATION_STATS_H
+
+#include "access/genam.h"
+
+enum relation_stats_argnum
+{
+ RELSTAT_RELPAGES,
+ RELSTAT_RELTUPLES,
+ RELSTAT_RELALLVISIBLE,
+ RELSTAT_RELALLFROZEN,
+ RELSTAT_NUM_RELSTATS
+};
+
+extern bool relation_statistics_update(Relation rel, const bool *isnull,
+ const char **values);
+
+#define RELATION_STATS_H
+#endif
--
2.50.1 (Apple Git-155)