From 4b832c5c733f6a919547287c9faf15ffa45d23c4 Mon Sep 17 00:00:00 2001 From: Corey Huinker Date: Sun, 28 Jun 2026 23:32:33 -0500 Subject: [PATCH v2 12/13] Add attribute_statistics_update, refactor update_attstats. Introduce a new enum attribute_stats_argnum, which is a subset of attribute_args_argnum but contains only the statistical values. Modify update_attstats to take a Relation argument, and index the NullableDatum array by the new enum attribute_stats_argnum. All processing and validation of non-statistical values like schema, relname, attname, attnum, as well as checking for recovery mode and acquiring the lock on the attribute are now handled in the calling functions pg_restore_attribute_stats and pg_clear_attribute_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 attribute_statistics_update which takes arguments for Relation, attname, attnum, inherited, and arrays of isnull and cstring values arguments, which will be translated into the Datum values required by update_attstats(). The end result is that all user-facing functions are able to call the same update_attstats(). --- src/backend/statistics/attribute_stats.c | 419 +++++++++++++++++------ src/include/statistics/attribute_stats.h | 45 +++ 2 files changed, 356 insertions(+), 108 deletions(-) create mode 100644 src/include/statistics/attribute_stats.h diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c index 24c3204e837..c557f524b9b 100644 --- a/src/backend/statistics/attribute_stats.c +++ b/src/backend/statistics/attribute_stats.c @@ -22,6 +22,7 @@ #include "catalog/namespace.h" #include "catalog/pg_operator.h" #include "nodes/makefuncs.h" +#include "statistics/attribute_stats.h" #include "statistics/statistics.h" #include "statistics/stat_utils.h" #include "utils/array.h" @@ -81,6 +82,31 @@ static struct StatsArgInfo attarginfo[] = [ATTARG_NUM_ATTARGS] = {0} }; +static struct StatsArgInfo attstatinfo[] = +{ + [ATTSTAT_NULL_FRAC] = {"null_frac", FLOAT4OID}, + [ATTSTAT_AVG_WIDTH] = {"avg_width", INT4OID}, + [ATTSTAT_N_DISTINCT] = {"n_distinct", FLOAT4OID}, + [ATTSTAT_MOST_COMMON_VALS] = {"most_common_vals", TEXTOID}, + [ATTSTAT_MOST_COMMON_FREQS] = {"most_common_freqs", FLOAT4ARRAYOID}, + [ATTSTAT_HISTOGRAM_BOUNDS] = {"histogram_bounds", TEXTOID}, + [ATTSTAT_CORRELATION] = {"correlation", FLOAT4OID}, + [ATTSTAT_MOST_COMMON_ELEMS] = {"most_common_elems", TEXTOID}, + [ATTSTAT_MOST_COMMON_ELEM_FREQS] = {"most_common_elem_freqs", FLOAT4ARRAYOID}, + [ATTSTAT_ELEM_COUNT_HISTOGRAM] = {"elem_count_histogram", FLOAT4ARRAYOID}, + [ATTSTAT_RANGE_LENGTH_HISTOGRAM] = {"range_length_histogram", TEXTOID}, + [ATTSTAT_RANGE_EMPTY_FRAC] = {"range_empty_frac", FLOAT4OID}, + [ATTSTAT_RANGE_BOUNDS_HISTOGRAM] = {"range_bounds_histogram", TEXTOID}, + [ATTSTAT_NUM_ATTSTATS] = {0} +}; + +/* + * The order of statisics in attribute_args_argnum is the same as + * attribute_stats_argnum so when mapping Datums from one to the other we + * can use this offset. + */ +#define ARGS_STATS_OFFSET (ATTARG_NUM_ATTARGS - ATTSTAT_NUM_ATTSTATS) + /* * Positional argument numbers, names, and types for * pg_clear_attribute_stats(). @@ -104,7 +130,8 @@ static struct StatsArgInfo cleararginfo[] = [C_ATTARG_NUM_ATTARGS] = {0} }; -static bool update_attstats(const NullableDatum *args); +static bool update_attstats(Relation rel, const char *attname, AttrNumber attnum, + bool inherited, const NullableDatum *args); static void upsert_pg_statistic(Relation starel, HeapTuple oldtup, const Datum *values, const bool *nulls, const bool *replaces); static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit); @@ -126,16 +153,9 @@ static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit); * and other statistic kinds may still be updated. */ static bool -update_attstats(const NullableDatum *args) +update_attstats(Relation rel, const char *attname, AttrNumber attnum, + bool inherited, const NullableDatum *args) { - char *nspname; - char *relname; - Oid reloid; - char *attname; - AttrNumber attnum; - bool inherited; - Oid locked_table = InvalidOid; - Relation starel; HeapTuple statup; @@ -151,16 +171,16 @@ update_attstats(const NullableDatum *args) FmgrInfo array_in_fn; - bool do_mcv = !args[ATTARG_MOST_COMMON_FREQS].isnull && - !args[ATTARG_MOST_COMMON_VALS].isnull; - bool do_histogram = !args[ATTARG_HISTOGRAM_BOUNDS].isnull; - bool do_correlation = !args[ATTARG_CORRELATION].isnull; - bool do_mcelem = !args[ATTARG_MOST_COMMON_ELEMS].isnull && - !args[ATTARG_MOST_COMMON_ELEM_FREQS].isnull; - bool do_dechist = !args[ATTARG_ELEM_COUNT_HISTOGRAM].isnull; - bool do_bounds_histogram = !args[ATTARG_RANGE_BOUNDS_HISTOGRAM].isnull; - bool do_range_length_histogram = !args[ATTARG_RANGE_LENGTH_HISTOGRAM].isnull && - !args[ATTARG_RANGE_EMPTY_FRAC].isnull; + bool do_mcv = !args[ATTSTAT_MOST_COMMON_FREQS].isnull && + !args[ATTSTAT_MOST_COMMON_VALS].isnull; + bool do_histogram = !args[ATTSTAT_HISTOGRAM_BOUNDS].isnull; + bool do_correlation = !args[ATTSTAT_CORRELATION].isnull; + bool do_mcelem = !args[ATTSTAT_MOST_COMMON_ELEMS].isnull && + !args[ATTSTAT_MOST_COMMON_ELEM_FREQS].isnull; + bool do_dechist = !args[ATTSTAT_ELEM_COUNT_HISTOGRAM].isnull; + bool do_bounds_histogram = !args[ATTSTAT_RANGE_BOUNDS_HISTOGRAM].isnull; + bool do_range_length_histogram = !args[ATTSTAT_RANGE_LENGTH_HISTOGRAM].isnull && + !args[ATTSTAT_RANGE_EMPTY_FRAC].isnull; Datum values[Natts_pg_statistic] = {0}; bool nulls[Natts_pg_statistic] = {0}; @@ -168,116 +188,59 @@ update_attstats(const NullableDatum *args) bool result = true; - stats_check_required_arg(args, attarginfo, ATTARG_ATTRELSCHEMA); - stats_check_required_arg(args, attarginfo, ATTARG_ATTRELNAME); - - nspname = TextDatumGetCString(args[ATTARG_ATTRELSCHEMA].value); - relname = TextDatumGetCString(args[ATTARG_ATTRELNAME].value); - - if (RecoveryInProgress()) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("recovery is in progress"), - errhint("Statistics cannot be modified during recovery."))); - - /* lock before looking up attribute */ - reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1), - ShareUpdateExclusiveLock, 0, - RangeVarCallbackForStats, &locked_table); - - /* user can specify either attname or attnum, but not both */ - if (!args[ATTARG_ATTNAME].isnull) - { - if (!args[ATTARG_ATTNUM].isnull) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot specify both \"%s\" and \"%s\"", "attname", "attnum"))); - attname = TextDatumGetCString(args[ATTARG_ATTNAME].value); - attnum = get_attnum(reloid, attname); - /* note that this test covers attisdropped cases too: */ - if (attnum == InvalidAttrNumber) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - attname, relname))); - } - else if (!args[ATTARG_ATTNUM].isnull) - { - attnum = DatumGetInt16(args[ATTARG_ATTNUM].value); - attname = get_attname(reloid, attnum, true); - /* annoyingly, get_attname doesn't check attisdropped */ - if (attname == NULL || - !SearchSysCacheExistsAttName(reloid, attname)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column %d of relation \"%s\" does not exist", - attnum, relname))); - } - else - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("must specify either \"%s\" or \"%s\"", "attname", "attnum"))); - attname = NULL; /* keep compiler quiet */ - attnum = 0; - } - if (attnum < 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot modify statistics on system column \"%s\"", attname))); - stats_check_required_arg(args, attarginfo, ATTARG_INHERITED); - inherited = DatumGetBool(args[ATTARG_INHERITED].value); - /* * Check argument sanity. If some arguments are unusable, emit a WARNING * and set the corresponding argument to NULL in fcinfo. */ - if (!stats_check_arg_array(args, attarginfo, ATTARG_MOST_COMMON_FREQS)) + if (!stats_check_arg_array(args, attstatinfo, ATTSTAT_MOST_COMMON_FREQS)) { do_mcv = false; result = false; } - if (!stats_check_arg_array(args, attarginfo, ATTARG_MOST_COMMON_ELEM_FREQS)) + if (!stats_check_arg_array(args, attstatinfo, ATTSTAT_MOST_COMMON_ELEM_FREQS)) { do_mcelem = false; result = false; } - if (!stats_check_arg_array(args, attarginfo, ATTARG_ELEM_COUNT_HISTOGRAM)) + if (!stats_check_arg_array(args, attstatinfo, ATTSTAT_ELEM_COUNT_HISTOGRAM)) { do_dechist = false; result = false; } - if (!stats_check_arg_pair(args, attarginfo, - ATTARG_MOST_COMMON_VALS, ATTARG_MOST_COMMON_FREQS)) + if (!stats_check_arg_pair(args, attstatinfo, + ATTSTAT_MOST_COMMON_VALS, ATTSTAT_MOST_COMMON_FREQS)) { do_mcv = false; result = false; } - if (!stats_check_arg_pair(args, attarginfo, - ATTARG_MOST_COMMON_ELEMS, - ATTARG_MOST_COMMON_ELEM_FREQS)) + if (!stats_check_arg_pair(args, attstatinfo, + ATTSTAT_MOST_COMMON_ELEMS, + ATTSTAT_MOST_COMMON_ELEM_FREQS)) { do_mcelem = false; result = false; } - if (!stats_check_arg_pair(args, attarginfo, - ATTARG_RANGE_LENGTH_HISTOGRAM, - ATTARG_RANGE_EMPTY_FRAC)) + if (!stats_check_arg_pair(args, attstatinfo, + ATTSTAT_RANGE_LENGTH_HISTOGRAM, + ATTSTAT_RANGE_EMPTY_FRAC)) { do_range_length_histogram = false; result = false; } /* derive information from attribute */ - statatt_get_type(reloid, attnum, + statatt_get_type(RelationGetRelid(rel), attnum, &atttypid, &atttypmod, &atttyptype, &atttypcoll, &eq_opr, <_opr); @@ -334,29 +297,29 @@ update_attstats(const NullableDatum *args) starel = table_open(StatisticRelationId, RowExclusiveLock); - statup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(reloid), Int16GetDatum(attnum), BoolGetDatum(inherited)); + statup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(RelationGetRelid(rel)), Int16GetDatum(attnum), BoolGetDatum(inherited)); /* initialize from existing tuple if exists */ if (HeapTupleIsValid(statup)) heap_deform_tuple(statup, RelationGetDescr(starel), values, nulls); else - statatt_init_empty_tuple(reloid, attnum, inherited, values, nulls, + statatt_init_empty_tuple(RelationGetRelid(rel), attnum, inherited, values, nulls, replaces); /* if specified, set to argument values */ - if (!args[ATTARG_NULL_FRAC].isnull) + if (!args[ATTSTAT_NULL_FRAC].isnull) { - values[Anum_pg_statistic_stanullfrac - 1] = args[ATTARG_NULL_FRAC].value; + values[Anum_pg_statistic_stanullfrac - 1] = args[ATTSTAT_NULL_FRAC].value; replaces[Anum_pg_statistic_stanullfrac - 1] = true; } - if (!args[ATTARG_AVG_WIDTH].isnull) + if (!args[ATTSTAT_AVG_WIDTH].isnull) { - values[Anum_pg_statistic_stawidth - 1] = args[ATTARG_AVG_WIDTH].value; + values[Anum_pg_statistic_stawidth - 1] = args[ATTSTAT_AVG_WIDTH].value; replaces[Anum_pg_statistic_stawidth - 1] = true; } - if (!args[ATTARG_N_DISTINCT].isnull) + if (!args[ATTSTAT_N_DISTINCT].isnull) { - values[Anum_pg_statistic_stadistinct - 1] = args[ATTARG_N_DISTINCT].value; + values[Anum_pg_statistic_stadistinct - 1] = args[ATTSTAT_N_DISTINCT].value; replaces[Anum_pg_statistic_stadistinct - 1] = true; } @@ -364,10 +327,10 @@ update_attstats(const NullableDatum *args) if (do_mcv) { bool converted; - Datum stanumbers = args[ATTARG_MOST_COMMON_FREQS].value; + Datum stanumbers = args[ATTSTAT_MOST_COMMON_FREQS].value; Datum stavalues = statatt_build_stavalues("most_common_vals", &array_in_fn, - args[ATTARG_MOST_COMMON_VALS].value, + args[ATTSTAT_MOST_COMMON_VALS].value, atttypid, atttypmod, &converted); @@ -407,7 +370,7 @@ update_attstats(const NullableDatum *args) stavalues = statatt_build_stavalues("histogram_bounds", &array_in_fn, - args[ATTARG_HISTOGRAM_BOUNDS].value, + args[ATTSTAT_HISTOGRAM_BOUNDS].value, atttypid, atttypmod, &converted); @@ -425,7 +388,7 @@ update_attstats(const NullableDatum *args) /* STATISTIC_KIND_CORRELATION */ if (do_correlation) { - Datum elems[] = {args[ATTARG_CORRELATION].value}; + Datum elems[] = {args[ATTSTAT_CORRELATION].value}; ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID); Datum stanumbers = PointerGetDatum(arry); @@ -438,13 +401,13 @@ update_attstats(const NullableDatum *args) /* STATISTIC_KIND_MCELEM */ if (do_mcelem) { - Datum stanumbers = args[ATTARG_MOST_COMMON_ELEM_FREQS].value; + Datum stanumbers = args[ATTSTAT_MOST_COMMON_ELEM_FREQS].value; bool converted = false; Datum stavalues; stavalues = statatt_build_stavalues("most_common_elems", &array_in_fn, - args[ATTARG_MOST_COMMON_ELEMS].value, + args[ATTSTAT_MOST_COMMON_ELEMS].value, elemtypid, atttypmod, &converted); @@ -462,7 +425,7 @@ update_attstats(const NullableDatum *args) /* STATISTIC_KIND_DECHIST */ if (do_dechist) { - Datum stanumbers = args[ATTARG_ELEM_COUNT_HISTOGRAM].value; + Datum stanumbers = args[ATTSTAT_ELEM_COUNT_HISTOGRAM].value; statatt_set_slot(values, nulls, replaces, STATISTIC_KIND_DECHIST, @@ -484,7 +447,7 @@ update_attstats(const NullableDatum *args) stavalues = statatt_build_stavalues("range_bounds_histogram", &array_in_fn, - args[ATTARG_RANGE_BOUNDS_HISTOGRAM].value, + args[ATTSTAT_RANGE_BOUNDS_HISTOGRAM].value, atttypid, atttypmod, &converted); @@ -503,7 +466,7 @@ update_attstats(const NullableDatum *args) if (do_range_length_histogram) { /* The anyarray is always a float8[] for this stakind */ - Datum elems[] = {args[ATTARG_RANGE_EMPTY_FRAC].value}; + Datum elems[] = {args[ATTSTAT_RANGE_EMPTY_FRAC].value}; ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID); Datum stanumbers = PointerGetDatum(arry); @@ -512,7 +475,7 @@ update_attstats(const NullableDatum *args) stavalues = statatt_build_stavalues("range_length_histogram", &array_in_fn, - args[ATTARG_RANGE_LENGTH_HISTOGRAM].value, + args[ATTSTAT_RANGE_LENGTH_HISTOGRAM].value, FLOAT8OID, 0, &converted); if (converted) @@ -674,14 +637,254 @@ Datum pg_restore_attribute_stats(PG_FUNCTION_ARGS) { NullableDatum positional_args[ATTARG_NUM_ATTARGS]; + NullableDatum stats[ATTSTAT_NUM_ATTSTATS]; + + char *nspname; + char *relname; + Oid reloid; + char *attname; + AttrNumber attnum; + bool inherited; + Oid locked_table = InvalidOid; + Relation rel; + bool result = true; if (!stats_fill_args_from_arg_pairs(fcinfo, positional_args, attarginfo)) result = false; - if (!update_attstats(positional_args)) + stats_check_required_arg(positional_args, attarginfo, ATTARG_ATTRELSCHEMA); + stats_check_required_arg(positional_args, attarginfo, ATTARG_ATTRELNAME); + + nspname = TextDatumGetCString(positional_args[ATTARG_ATTRELSCHEMA].value); + relname = TextDatumGetCString(positional_args[ATTARG_ATTRELNAME].value); + + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is in progress"), + errhint("Statistics cannot be modified during recovery."))); + + /* lock before looking up attribute */ + reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1), + ShareUpdateExclusiveLock, 0, + RangeVarCallbackForStats, &locked_table); + rel = relation_open(reloid, NoLock); + + /* user can specify either attname or attnum, but not both */ + if (!positional_args[ATTARG_ATTNAME].isnull) + { + if (!positional_args[ATTARG_ATTNUM].isnull) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot specify both \"%s\" and \"%s\"", "attname", "attnum"))); + attname = TextDatumGetCString(positional_args[ATTARG_ATTNAME].value); + attnum = get_attnum(reloid, attname); + /* note that this test covers attisdropped cases too: */ + if (attnum == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + attname, relname))); + } + else if (!positional_args[ATTARG_ATTNUM].isnull) + { + attnum = DatumGetInt16(positional_args[ATTARG_ATTNUM].value); + attname = get_attname(reloid, attnum, true); + /* annoyingly, get_attname doesn't check attisdropped */ + if (attname == NULL || + !SearchSysCacheExistsAttName(reloid, attname)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %d of relation \"%s\" does not exist", + attnum, relname))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("must specify either \"%s\" or \"%s\"", "attname", "attnum"))); + attname = NULL; /* keep compiler quiet */ + attnum = 0; + } + + stats_check_required_arg(positional_args, attarginfo, ATTARG_INHERITED); + inherited = DatumGetBool(positional_args[ATTARG_INHERITED].value); + + /* Map ATTARGs to ATTSTATs */ + for (int i = 0; i < ATTSTAT_NUM_ATTSTATS; i++) + { + stats[i].isnull = positional_args[i + ARGS_STATS_OFFSET].isnull; + stats[i].value = positional_args[i + ARGS_STATS_OFFSET].value; + } + + if (!update_attstats(rel, attname, attnum, inherited, stats)) result = false; + relation_close(rel, NoLock); + PG_RETURN_BOOL(result); } + +/* + * Delete the pg_statistic for a given relation's attnum+inherited. + */ +bool +attribute_statistics_delete(Relation rel, AttrNumber attnum, bool inherited) +{ + return delete_pg_statistic(RelationGetRelid(rel), attnum, inherited); +} + +/* + * 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; + } + } +} + +/* + * Convenience routine to parse int4 values, and emit a warning on parse + * errors. + */ +static void +str_to_int4(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(int4in, (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; + } + } +} + +/* + * Convenience routine to parse float values, and emit a warning on parse + * errors. + */ +static void +str_to_text(NullableDatum *stats, int statnum, const bool *isnull, + const char **values) +{ + stats[statnum].isnull = true; + stats[statnum].value = 0; + + if (!isnull[statnum]) + { + stats[statnum].isnull = false; + stats[statnum].value = CStringGetTextDatum(values[statnum]); + } +} + +/* + * Convenience routine to parse float array values, and emit a warning on parse + * errors. + */ +static void +str_to_floatarray(NullableDatum *stats, int statnum, const bool *isnull, + const char **values) +{ + stats[statnum].isnull = true; + stats[statnum].value = 0; + + if (!isnull[statnum]) + { + ErrorSaveContext escontext = {T_ErrorSaveContext}; + + FmgrInfo flinfo; + Datum value; + + fmgr_info(F_ARRAY_IN, &flinfo); + + if (!InputFunctionCallSafe(&flinfo, (char *) values[statnum], FLOAT4OID, + -1, (Node *) &escontext, &value)) + { + escontext.error_data->elevel = WARNING; + ThrowErrorData(escontext.error_data); + FreeErrorData(escontext.error_data); + } + else + { + stats[statnum].isnull = false; + stats[statnum].value = value; + } + } +} + +/* + * Update statistics for a given attribute+inherited of already opened Relation + * with a lock level of at least ShareUpdateExclusiveLock. + */ +bool +attribute_statistics_update(Relation rel, const char *attname, + AttrNumber attnum, bool inherited, + const bool *isnull, const char **values) +{ + NullableDatum stats[ATTSTAT_NUM_ATTSTATS]; + + str_to_float(stats, ATTSTAT_NULL_FRAC, isnull, values); + str_to_int4(stats, ATTSTAT_AVG_WIDTH, isnull, values); + str_to_float(stats, ATTSTAT_N_DISTINCT, isnull, values); + str_to_text(stats, ATTSTAT_MOST_COMMON_VALS, isnull, values); + str_to_floatarray(stats, ATTSTAT_MOST_COMMON_FREQS, isnull, values); + str_to_text(stats, ATTSTAT_HISTOGRAM_BOUNDS, isnull, values); + str_to_float(stats, ATTSTAT_CORRELATION, isnull, values); + str_to_text(stats, ATTSTAT_MOST_COMMON_ELEMS, isnull, values); + str_to_floatarray(stats, ATTSTAT_MOST_COMMON_ELEM_FREQS, isnull, values); + str_to_floatarray(stats, ATTSTAT_ELEM_COUNT_HISTOGRAM, isnull, values); + str_to_text(stats, ATTSTAT_RANGE_LENGTH_HISTOGRAM, isnull, values); + str_to_float(stats, ATTSTAT_RANGE_EMPTY_FRAC, isnull, values); + str_to_text(stats, ATTSTAT_RANGE_BOUNDS_HISTOGRAM, isnull, values); + + return update_attstats(rel, attname, attnum, inherited, stats); +} diff --git a/src/include/statistics/attribute_stats.h b/src/include/statistics/attribute_stats.h new file mode 100644 index 00000000000..efbfd4222f7 --- /dev/null +++ b/src/include/statistics/attribute_stats.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * attribute_stats.h + * Functions for the internal manipulation of attribute statistics. + * + * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/statistics/attribute_stats.h + * + *------------------------------------------------------------------------- + */ +#include "access/attnum.h" +#ifndef ATTRIBUTE_STATS_H + +#include "access/genam.h" + +enum attribute_stats_argnum +{ + ATTSTAT_NULL_FRAC, + ATTSTAT_AVG_WIDTH, + ATTSTAT_N_DISTINCT, + ATTSTAT_MOST_COMMON_VALS, + ATTSTAT_MOST_COMMON_FREQS, + ATTSTAT_HISTOGRAM_BOUNDS, + ATTSTAT_CORRELATION, + ATTSTAT_MOST_COMMON_ELEMS, + ATTSTAT_MOST_COMMON_ELEM_FREQS, + ATTSTAT_ELEM_COUNT_HISTOGRAM, + ATTSTAT_RANGE_LENGTH_HISTOGRAM, + ATTSTAT_RANGE_EMPTY_FRAC, + ATTSTAT_RANGE_BOUNDS_HISTOGRAM, + ATTSTAT_NUM_ATTSTATS +}; + +extern bool attribute_statistics_delete(Relation rel, AttrNumber attnum, + bool inherited); + +extern bool attribute_statistics_update(Relation rel, const char *attname, + AttrNumber attnum, bool inherited, + const bool *isnull, const char **values); + + +#define ATTRIBUTE_STATS_H +#endif -- 2.50.1 (Apple Git-155)