diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/statistics/attribute_stats.c | 19 | ||||
-rw-r--r-- | src/backend/statistics/relation_stats.c | 60 | ||||
-rw-r--r-- | src/backend/statistics/stat_utils.c | 126 |
3 files changed, 181 insertions, 24 deletions
diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c index 979fe41a000..af61fd79e49 100644 --- a/src/backend/statistics/attribute_stats.c +++ b/src/backend/statistics/attribute_stats.c @@ -877,3 +877,22 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS) delete_pg_statistic(reloid, attnum, inherited); PG_RETURN_VOID(); } + +Datum +pg_restore_attribute_stats(PG_FUNCTION_ARGS) +{ + LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS); + bool result = true; + + InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS, + InvalidOid, NULL, NULL); + + if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, + attarginfo, WARNING)) + result = false; + + if (!attribute_statistics_update(positional_fcinfo, WARNING)) + result = false; + + PG_RETURN_BOOL(result); +} diff --git a/src/backend/statistics/relation_stats.c b/src/backend/statistics/relation_stats.c index b1eb8a9bbaf..5a2aabc921e 100644 --- a/src/backend/statistics/relation_stats.c +++ b/src/backend/statistics/relation_stats.c @@ -67,8 +67,7 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel) bool nulls[3] = {0}; int ncols = 0; TupleDesc tupdesc; - HeapTuple newtup; - + bool result = true; stats_check_required_arg(fcinfo, relarginfo, RELATION_ARG); reloid = PG_GETARG_OID(RELATION_ARG); @@ -109,11 +108,9 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel) ereport(elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("relpages cannot be < -1"))); - table_close(crel, RowExclusiveLock); - return false; + result = false; } - - if (relpages != pgcform->relpages) + else if (relpages != pgcform->relpages) { replaces[ncols] = Anum_pg_class_relpages; values[ncols] = Int32GetDatum(relpages); @@ -130,16 +127,15 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel) ereport(elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("reltuples cannot be < -1.0"))); - table_close(crel, RowExclusiveLock); - return false; + result = false; } - - if (reltuples != pgcform->reltuples) + else if (reltuples != pgcform->reltuples) { replaces[ncols] = Anum_pg_class_reltuples; values[ncols] = Float4GetDatum(reltuples); ncols++; } + } if (!PG_ARGISNULL(RELALLVISIBLE_ARG)) @@ -151,11 +147,9 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel) ereport(elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("relallvisible cannot be < 0"))); - table_close(crel, RowExclusiveLock); - return false; + result = false; } - - if (relallvisible != pgcform->relallvisible) + else if (relallvisible != pgcform->relallvisible) { replaces[ncols] = Anum_pg_class_relallvisible; values[ncols] = Int32GetDatum(relallvisible); @@ -164,22 +158,20 @@ relation_statistics_update(FunctionCallInfo fcinfo, int elevel) } /* only update pg_class if there is a meaningful change */ - if (ncols == 0) + if (ncols > 0) { - table_close(crel, RowExclusiveLock); - return false; - } - - newtup = heap_modify_tuple_by_cols(ctup, tupdesc, ncols, replaces, values, - nulls); + HeapTuple newtup; - CatalogTupleUpdate(crel, &newtup->t_self, newtup); - heap_freetuple(newtup); + newtup = heap_modify_tuple_by_cols(ctup, tupdesc, ncols, replaces, values, + nulls); + CatalogTupleUpdate(crel, &newtup->t_self, newtup); + heap_freetuple(newtup); + } /* release the lock, consistent with vac_update_relstats() */ table_close(crel, RowExclusiveLock); - return true; + return result; } /* @@ -215,3 +207,23 @@ pg_clear_relation_stats(PG_FUNCTION_ARGS) relation_statistics_update(newfcinfo, ERROR); PG_RETURN_VOID(); } + +Datum +pg_restore_relation_stats(PG_FUNCTION_ARGS) +{ + LOCAL_FCINFO(positional_fcinfo, NUM_RELATION_STATS_ARGS); + bool result = true; + + InitFunctionCallInfoData(*positional_fcinfo, NULL, + NUM_RELATION_STATS_ARGS, + InvalidOid, NULL, NULL); + + if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, + relarginfo, WARNING)) + result = false; + + if (!relation_statistics_update(positional_fcinfo, WARNING)) + result = false; + + PG_RETURN_BOOL(result); +} diff --git a/src/backend/statistics/stat_utils.c b/src/backend/statistics/stat_utils.c index 64b5f21ad8b..728e30e780c 100644 --- a/src/backend/statistics/stat_utils.c +++ b/src/backend/statistics/stat_utils.c @@ -18,6 +18,7 @@ #include "access/relation.h" #include "catalog/pg_database.h" +#include "funcapi.h" #include "miscadmin.h" #include "statistics/stat_utils.h" #include "utils/acl.h" @@ -165,3 +166,128 @@ stats_lock_check_privileges(Oid reloid) relation_close(rel, NoLock); } + +/* + * Find the argument number for the given argument name, returning -1 if not + * found. + */ +static int +get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo, int elevel) +{ + int argnum; + + for (argnum = 0; arginfo[argnum].argname != NULL; argnum++) + if (pg_strcasecmp(argname, arginfo[argnum].argname) == 0) + return argnum; + + ereport(elevel, + (errmsg("unrecognized argument name: \"%s\"", argname))); + + return -1; +} + +/* + * Ensure that a given argument matched the expected type. + */ +static bool +stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype, int elevel) +{ + if (argtype != expectedtype) + { + ereport(elevel, + (errmsg("argument \"%s\" has type \"%s\", expected type \"%s\"", + argname, format_type_be(argtype), + format_type_be(expectedtype)))); + return false; + } + + return true; +} + +/* + * Translate variadic argument pairs from 'pairs_fcinfo' into a + * 'positional_fcinfo' appropriate for calling relation_statistics_update() or + * attribute_statistics_update() with positional arguments. + * + * Caller should have already initialized positional_fcinfo with a size + * appropriate for calling the intended positional function, and arginfo + * should also match the intended positional function. + */ +bool +stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo, + FunctionCallInfo positional_fcinfo, + struct StatsArgInfo *arginfo, + int elevel) +{ + Datum *args; + bool *argnulls; + Oid *types; + int nargs; + bool result = true; + + /* clear positional args */ + for (int i = 0; arginfo[i].argname != NULL; i++) + { + positional_fcinfo->args[i].value = (Datum) 0; + positional_fcinfo->args[i].isnull = true; + } + + nargs = extract_variadic_args(pairs_fcinfo, 0, true, + &args, &types, &argnulls); + + if (nargs % 2 != 0) + ereport(ERROR, + errmsg("variadic arguments must be name/value pairs"), + errhint("Provide an even number of variadic arguments that can be divided into pairs.")); + + /* + * For each argument name/value pair, find corresponding positional + * argument for the argument name, and assign the argument value to + * postitional_fcinfo. + */ + for (int i = 0; i < nargs; i += 2) + { + int argnum; + char *argname; + + if (argnulls[i]) + ereport(ERROR, + (errmsg("name at variadic position %d is NULL", i + 1))); + + if (types[i] != TEXTOID) + ereport(ERROR, + (errmsg("name at variadic position %d has type \"%s\", expected type \"%s\"", + i + 1, format_type_be(types[i]), + format_type_be(TEXTOID)))); + + if (argnulls[i + 1]) + continue; + + argname = TextDatumGetCString(args[i]); + + /* + * The 'version' argument is a special case, not handled by arginfo + * because it's not a valid positional argument. + * + * For now, 'version' is accepted but ignored. In the future it can be + * used to interpret older statistics properly. + */ + if (pg_strcasecmp(argname, "version") == 0) + continue; + + argnum = get_arg_by_name(argname, arginfo, elevel); + + if (argnum < 0 || !stats_check_arg_type(argname, types[i + 1], + arginfo[argnum].argtype, + elevel)) + { + result = false; + continue; + } + + positional_fcinfo->args[argnum].value = args[i + 1]; + positional_fcinfo->args[argnum].isnull = false; + } + + return result; +} |