</para>
</entry>
</row>
+ <row>
+ <entry role="func_table_entry">
+ <para role="func_signature">
+ <indexterm>
+ <primary>pg_clear_extended_stats</primary>
+ </indexterm>
+ <function>pg_clear_extended_stats</function> (
+ <parameter>schemaname</parameter> <type>name</type>,
+ <parameter>relname</parameter> <type>name</type>,
+ <parameter>statistics_schemaname</parameter> <type>name</type>,
+ <parameter>statistics_name</parameter> <type>name</type>,
+ <parameter>inherited</parameter> <type>boolean</type> )
+ <returnvalue>void</returnvalue>
+ </para>
+ <para>
+ Clears data of an extended statistics object, as though the object
+ was newly-created. The required arguments are
+ <literal>schemaname</literal> and <literal>relname</literal> to
+ specify the schema and table name of the relation whose statistics
+ are cleared, as well as <literal>statistics_schemaname</literal>
+ and <literal>statistics_name</literal> to specify the schema and
+ extended statistics name of the extended statistics object to clear.
+ </para>
+ <para>
+ The caller must have the <literal>MAINTAIN</literal> privilege on
+ the table or be the owner of the database.
+ </para>
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
attribute_stats.o \
dependencies.o \
extended_stats.o \
+ extended_stats_funcs.o \
mcv.o \
mvdistinct.o \
relation_stats.o \
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * extended_stats_funcs.c
+ * Functions for manipulating extended statistics.
+ *
+ * This file includes the set of facilities required to support the direct
+ * manipulations of extended statistics objects.
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/statistics/extended_stats_funcs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_statistic_ext.h"
+#include "catalog/pg_statistic_ext_data.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "statistics/stat_utils.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Index of the arguments for the SQL functions.
+ */
+enum extended_stats_argnum
+{
+ RELSCHEMA_ARG = 0,
+ RELNAME_ARG,
+ STATSCHEMA_ARG,
+ STATNAME_ARG,
+ INHERITED_ARG,
+ NUM_EXTENDED_STATS_ARGS,
+};
+
+/*
+ * The argument names and type OIDs of the arguments for the SQL
+ * functions.
+ */
+static struct StatsArgInfo extarginfo[] =
+{
+ [RELSCHEMA_ARG] = {"schemaname", TEXTOID},
+ [RELNAME_ARG] = {"relname", TEXTOID},
+ [STATSCHEMA_ARG] = {"statistics_schemaname", TEXTOID},
+ [STATNAME_ARG] = {"statistics_name", TEXTOID},
+ [INHERITED_ARG] = {"inherited", BOOLOID},
+ [NUM_EXTENDED_STATS_ARGS] = {0},
+};
+
+static HeapTuple get_pg_statistic_ext(Relation pg_stext, Oid nspoid,
+ const char *stxname);
+static bool delete_pg_statistic_ext_data(Oid stxoid, bool inherited);
+
+/*
+ * Fetch a pg_statistic_ext row by name and namespace OID.
+ */
+static HeapTuple
+get_pg_statistic_ext(Relation pg_stext, Oid nspoid, const char *stxname)
+{
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+ Oid stxoid = InvalidOid;
+
+ ScanKeyInit(&key[0],
+ Anum_pg_statistic_ext_stxname,
+ BTEqualStrategyNumber,
+ F_NAMEEQ,
+ CStringGetDatum(stxname));
+ ScanKeyInit(&key[1],
+ Anum_pg_statistic_ext_stxnamespace,
+ BTEqualStrategyNumber,
+ F_OIDEQ,
+ ObjectIdGetDatum(nspoid));
+
+ /*
+ * Try to find matching pg_statistic_ext row.
+ */
+ scan = systable_beginscan(pg_stext,
+ StatisticExtNameIndexId,
+ true,
+ NULL,
+ 2,
+ key);
+
+ /* Lookup is based on a unique index, so we get either 0 or 1 tuple. */
+ tup = systable_getnext(scan);
+
+ if (HeapTupleIsValid(tup))
+ stxoid = ((Form_pg_statistic_ext) GETSTRUCT(tup))->oid;
+
+ systable_endscan(scan);
+
+ if (!OidIsValid(stxoid))
+ return NULL;
+
+ return SearchSysCacheCopy1(STATEXTOID, ObjectIdGetDatum(stxoid));
+}
+
+/*
+ * Remove an existing pg_statistic_ext_data row for a given pg_statistic_ext
+ * row and "inherited" pair.
+ */
+static bool
+delete_pg_statistic_ext_data(Oid stxoid, bool inherited)
+{
+ Relation sed = table_open(StatisticExtDataRelationId, RowExclusiveLock);
+ HeapTuple oldtup;
+ bool result = false;
+
+ /* Is there already a pg_statistic tuple for this attribute? */
+ oldtup = SearchSysCache2(STATEXTDATASTXOID,
+ ObjectIdGetDatum(stxoid),
+ BoolGetDatum(inherited));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ CatalogTupleDelete(sed, &oldtup->t_self);
+ ReleaseSysCache(oldtup);
+ result = true;
+ }
+
+ table_close(sed, RowExclusiveLock);
+
+ CommandCounterIncrement();
+
+ return result;
+}
+
+/*
+ * Delete statistics for the given statistics object.
+ */
+Datum
+pg_clear_extended_stats(PG_FUNCTION_ARGS)
+{
+ char *relnspname;
+ char *relname;
+ char *nspname;
+ Oid nspoid;
+ Oid relid;
+ char *stxname;
+ bool inherited;
+ Relation pg_stext;
+ HeapTuple tup;
+ Form_pg_statistic_ext stxform;
+ Oid locked_table = InvalidOid;
+
+ /* relation arguments */
+ stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
+ relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
+ stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
+ relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
+
+ /* extended statistics arguments */
+ stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
+ nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
+ stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
+ stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
+ stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
+ inherited = PG_GETARG_BOOL(INHERITED_ARG);
+
+ if (RecoveryInProgress())
+ {
+ ereport(WARNING,
+ errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is in progress"),
+ errhint("Statistics cannot be modified during recovery."));
+ PG_RETURN_VOID();
+ }
+
+ /*
+ * First open the relation where we expect to find the statistics. This
+ * is similar to relation and attribute statistics, so as ACL checks are
+ * done before any locks are taken, even before any attempts related to
+ * the extended stats object.
+ */
+ relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
+ ShareUpdateExclusiveLock, 0,
+ RangeVarCallbackForStats, &locked_table);
+
+ /* Now check if the namespace of the stats object exists. */
+ nspoid = get_namespace_oid(nspname, true);
+ if (nspoid == InvalidOid)
+ {
+ ereport(WARNING,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find schema \"%s\"", nspname));
+ PG_RETURN_VOID();
+ }
+
+ pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
+ tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ table_close(pg_stext, RowExclusiveLock);
+ ereport(WARNING,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find extended statistics object \"%s\".\"%s\"",
+ nspname, stxname));
+ PG_RETURN_VOID();
+ }
+
+ stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
+
+ /*
+ * This should be consistent, based on the lock taken on the table when we
+ * started.
+ */
+ if (stxform->stxrelid != relid)
+ elog(ERROR, "cache lookup failed for extended stats %u: found relation %u but expected %u",
+ stxform->oid, stxform->stxrelid, relid);
+
+ delete_pg_statistic_ext_data(stxform->oid, inherited);
+ heap_freetuple(tup);
+
+ table_close(pg_stext, RowExclusiveLock);
+
+ PG_RETURN_VOID();
+}
'attribute_stats.c',
'dependencies.c',
'extended_stats.c',
+ 'extended_stats_funcs.c',
'mcv.c',
'mvdistinct.c',
'relation_stats.c',
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202601081
+#define CATALOG_VERSION_NO 202601161
#endif
proname => 'gist_translate_cmptype_common', prorettype => 'int2',
proargtypes => 'int4', prosrc => 'gist_translate_cmptype_common' },
+# Extended Statistics functions
+{ oid => '9948', descr => 'clear statistics on extended statistics object',
+ proname => 'pg_clear_extended_stats', proisstrict => 'f', provolatile => 'v',
+ proparallel => 'u', prorettype => 'void', proargtypes => 'text text text text bool',
+ proargnames => '{schemaname,relname,statistics_schemaname,statistics_name,inherited}',
+ prosrc => 'pg_clear_extended_stats' },
+
# AIO related functions
{ oid => '6399', descr => 'information about in-progress asynchronous IOs',
proname => 'pg_get_aios', prorows => '100', proretset => 't',
PARTITION OF stats_import.part_parent
FOR VALUES FROM (0) TO (10)
WITH (autovacuum_enabled = false);
+-- This ensures the presence of extended statistics marked with
+-- inherited = true.
+CREATE STATISTICS stats_import.part_parent_stat
+ ON i, (i % 2)
+ FROM stats_import.part_parent;
CREATE INDEX part_parent_i ON stats_import.part_parent(i);
+INSERT INTO stats_import.part_parent
+SELECT g.g
+FROM generate_series(0,9) AS g(g);
+SELECT COUNT(*) FROM stats_import.part_parent;
+ count
+-------
+ 10
+(1 row)
+
+SELECT COUNT(*) FROM stats_import.part_child_1;
+ count
+-------
+ 10
+(1 row)
+
ANALYZE stats_import.part_parent;
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'part_parent_stat' GROUP BY e.inherited;
+ count | inherited
+-------+-----------
+ 1 | t
+(1 row)
+
SELECT relpages
FROM pg_class
WHERE oid = 'stats_import.part_parent'::regclass;
UNION ALL
SELECT 4, 'four', NULL, int4range(0,100), NULL;
CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1));
+CREATE STATISTICS stats_import.test_stat
+ ON name, comp, lower(arange), array_length(tags,1)
+ FROM stats_import.test;
-- Generate statistics on table with data
ANALYZE stats_import.test;
CREATE TABLE stats_import.test_clone ( LIKE stats_import.test )
WITH (autovacuum_enabled = false);
CREATE INDEX is_odd_clone ON stats_import.test_clone(((comp).a % 2 = 1));
+CREATE STATISTICS stats_import.test_stat_clone
+ ON name, comp, lower(arange), array_length(tags,1)
+ FROM stats_import.test_clone;
--
-- Copy stats from test to test_clone, and is_odd to is_odd_clone
--
(1 row)
DROP TABLE stats_temp;
+-- Tests for pg_clear_extended_stats().
+-- Invalid argument values.
+SELECT pg_clear_extended_stats(schemaname => NULL,
+ relname => 'rel_foo',
+ statistics_schemaname => 'schema_foo',
+ statistics_name => 'stat_bar',
+ inherited => false);
+ERROR: argument "schemaname" must not be null
+SELECT pg_clear_extended_stats(schemaname => 'schema_foo',
+ relname => NULL,
+ statistics_schemaname => 'schema_foo',
+ statistics_name => 'stat_bar',
+ inherited => false);
+ERROR: argument "relname" must not be null
+SELECT pg_clear_extended_stats(schemaname => 'schema_foo',
+ relname => 'rel_foo',
+ statistics_schemaname => NULL,
+ statistics_name => 'stat_bar',
+ inherited => false);
+ERROR: argument "statistics_schemaname" must not be null
+SELECT pg_clear_extended_stats(schemaname => 'schema_foo',
+ relname => 'rel_foo',
+ statistics_schemaname => 'schema_foo',
+ statistics_name => NULL,
+ inherited => false);
+ERROR: argument "statistics_name" must not be null
+SELECT pg_clear_extended_stats(schemaname => 'schema_foo',
+ relname => 'rel_foo',
+ statistics_schemaname => 'schema_foo',
+ statistics_name => 'stat_bar',
+ inherited => NULL);
+ERROR: argument "inherited" must not be null
+-- Missing objects
+SELECT pg_clear_extended_stats(schemaname => 'schema_not_exist',
+ relname => 'test',
+ statistics_schemaname => 'schema_not_exist',
+ statistics_name => 'test_stat',
+ inherited => false);
+ERROR: schema "schema_not_exist" does not exist
+SELECT pg_clear_extended_stats(schemaname => 'stats_import',
+ relname => 'table_not_exist',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'test_stat',
+ inherited => false);
+ERROR: relation "stats_import.table_not_exist" does not exist
+SELECT pg_clear_extended_stats(schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'schema_not_exist',
+ statistics_name => 'test_stat',
+ inherited => false);
+WARNING: could not find schema "schema_not_exist"
+ pg_clear_extended_stats
+-------------------------
+
+(1 row)
+
+SELECT pg_clear_extended_stats(schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'ext_stats_not_exist',
+ inherited => false);
+WARNING: could not find extended statistics object "stats_import"."ext_stats_not_exist"
+ pg_clear_extended_stats
+-------------------------
+
+(1 row)
+
+-- Check that records are removed after a valid clear call.
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'test_stat' GROUP BY e.inherited;
+ count | inherited
+-------+-----------
+ 1 | f
+(1 row)
+
+SELECT COUNT(*), e.inherited FROM pg_stats_ext_exprs AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'test_stat' GROUP BY e.inherited;
+ count | inherited
+-------+-----------
+ 2 | f
+(1 row)
+
+BEGIN;
+SELECT pg_catalog.pg_clear_extended_stats(
+ schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'test_stat',
+ inherited => false);
+ pg_clear_extended_stats
+-------------------------
+
+(1 row)
+
+SELECT mode FROM pg_locks WHERE locktype = 'relation' AND
+ relation = 'stats_import.test'::regclass AND
+ pid = pg_backend_pid();
+ mode
+--------------------------
+ ShareUpdateExclusiveLock
+(1 row)
+
+COMMIT;
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'test_stat' GROUP BY e.inherited;
+ count | inherited
+-------+-----------
+(0 rows)
+
+SELECT COUNT(*), e.inherited FROM pg_stats_ext_exprs AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'test_stat' GROUP BY e.inherited;
+ count | inherited
+-------+-----------
+ 2 |
+(1 row)
+
+-- And before/after on inherited stats
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'part_parent_stat' GROUP BY e.inherited;
+ count | inherited
+-------+-----------
+ 1 | t
+(1 row)
+
+SELECT pg_catalog.pg_clear_extended_stats(
+ schemaname => 'stats_import',
+ relname => 'part_parent',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'part_parent_stat',
+ inherited => true);
+ pg_clear_extended_stats
+-------------------------
+
+(1 row)
+
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'part_parent_stat' GROUP BY e.inherited;
+ count | inherited
+-------+-----------
+(0 rows)
+
+-- Check that MAINTAIN is required when clearing statistics.
+CREATE ROLE regress_test_extstat_clear;
+GRANT ALL ON SCHEMA stats_import TO regress_test_extstat_clear;
+SET ROLE regress_test_extstat_clear;
+SELECT pg_catalog.pg_clear_extended_stats(
+ schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'test_stat',
+ inherited => false);
+ERROR: permission denied for table test
+RESET ROLE;
+GRANT MAINTAIN ON stats_import.test TO regress_test_extstat_clear;
+SET ROLE regress_test_extstat_clear;
+SELECT pg_catalog.pg_clear_extended_stats(
+ schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'test_stat',
+ inherited => false);
+ pg_clear_extended_stats
+-------------------------
+
+(1 row)
+
+RESET ROLE;
+REVOKE MAINTAIN ON stats_import.test FROM regress_test_extstat_clear;
+REVOKE ALL ON SCHEMA stats_import FROM regress_test_extstat_clear;
+DROP ROLE regress_test_extstat_clear;
DROP SCHEMA stats_import CASCADE;
NOTICE: drop cascades to 6 other objects
DETAIL: drop cascades to type stats_import.complex_type
FOR VALUES FROM (0) TO (10)
WITH (autovacuum_enabled = false);
+-- This ensures the presence of extended statistics marked with
+-- inherited = true.
+CREATE STATISTICS stats_import.part_parent_stat
+ ON i, (i % 2)
+ FROM stats_import.part_parent;
+
CREATE INDEX part_parent_i ON stats_import.part_parent(i);
+INSERT INTO stats_import.part_parent
+SELECT g.g
+FROM generate_series(0,9) AS g(g);
+
+SELECT COUNT(*) FROM stats_import.part_parent;
+SELECT COUNT(*) FROM stats_import.part_child_1;
+
ANALYZE stats_import.part_parent;
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'part_parent_stat' GROUP BY e.inherited;
+
SELECT relpages
FROM pg_class
WHERE oid = 'stats_import.part_parent'::regclass;
CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1));
+CREATE STATISTICS stats_import.test_stat
+ ON name, comp, lower(arange), array_length(tags,1)
+ FROM stats_import.test;
+
-- Generate statistics on table with data
ANALYZE stats_import.test;
CREATE INDEX is_odd_clone ON stats_import.test_clone(((comp).a % 2 = 1));
+CREATE STATISTICS stats_import.test_stat_clone
+ ON name, comp, lower(arange), array_length(tags,1)
+ FROM stats_import.test_clone;
+
--
-- Copy stats from test to test_clone, and is_odd to is_odd_clone
--
AND inherited = false
AND attname = 'i';
DROP TABLE stats_temp;
+
+-- Tests for pg_clear_extended_stats().
+-- Invalid argument values.
+SELECT pg_clear_extended_stats(schemaname => NULL,
+ relname => 'rel_foo',
+ statistics_schemaname => 'schema_foo',
+ statistics_name => 'stat_bar',
+ inherited => false);
+SELECT pg_clear_extended_stats(schemaname => 'schema_foo',
+ relname => NULL,
+ statistics_schemaname => 'schema_foo',
+ statistics_name => 'stat_bar',
+ inherited => false);
+SELECT pg_clear_extended_stats(schemaname => 'schema_foo',
+ relname => 'rel_foo',
+ statistics_schemaname => NULL,
+ statistics_name => 'stat_bar',
+ inherited => false);
+SELECT pg_clear_extended_stats(schemaname => 'schema_foo',
+ relname => 'rel_foo',
+ statistics_schemaname => 'schema_foo',
+ statistics_name => NULL,
+ inherited => false);
+SELECT pg_clear_extended_stats(schemaname => 'schema_foo',
+ relname => 'rel_foo',
+ statistics_schemaname => 'schema_foo',
+ statistics_name => 'stat_bar',
+ inherited => NULL);
+-- Missing objects
+SELECT pg_clear_extended_stats(schemaname => 'schema_not_exist',
+ relname => 'test',
+ statistics_schemaname => 'schema_not_exist',
+ statistics_name => 'test_stat',
+ inherited => false);
+SELECT pg_clear_extended_stats(schemaname => 'stats_import',
+ relname => 'table_not_exist',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'test_stat',
+ inherited => false);
+SELECT pg_clear_extended_stats(schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'schema_not_exist',
+ statistics_name => 'test_stat',
+ inherited => false);
+SELECT pg_clear_extended_stats(schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'ext_stats_not_exist',
+ inherited => false);
+
+-- Check that records are removed after a valid clear call.
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'test_stat' GROUP BY e.inherited;
+SELECT COUNT(*), e.inherited FROM pg_stats_ext_exprs AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'test_stat' GROUP BY e.inherited;
+BEGIN;
+SELECT pg_catalog.pg_clear_extended_stats(
+ schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'test_stat',
+ inherited => false);
+SELECT mode FROM pg_locks WHERE locktype = 'relation' AND
+ relation = 'stats_import.test'::regclass AND
+ pid = pg_backend_pid();
+COMMIT;
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'test_stat' GROUP BY e.inherited;
+SELECT COUNT(*), e.inherited FROM pg_stats_ext_exprs AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'test_stat' GROUP BY e.inherited;
+-- And before/after on inherited stats
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'part_parent_stat' GROUP BY e.inherited;
+SELECT pg_catalog.pg_clear_extended_stats(
+ schemaname => 'stats_import',
+ relname => 'part_parent',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'part_parent_stat',
+ inherited => true);
+SELECT COUNT(*), e.inherited FROM pg_stats_ext AS e
+ WHERE e.statistics_schemaname = 'stats_import' AND
+ e.statistics_name = 'part_parent_stat' GROUP BY e.inherited;
+
+-- Check that MAINTAIN is required when clearing statistics.
+CREATE ROLE regress_test_extstat_clear;
+GRANT ALL ON SCHEMA stats_import TO regress_test_extstat_clear;
+SET ROLE regress_test_extstat_clear;
+SELECT pg_catalog.pg_clear_extended_stats(
+ schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'test_stat',
+ inherited => false);
+RESET ROLE;
+GRANT MAINTAIN ON stats_import.test TO regress_test_extstat_clear;
+SET ROLE regress_test_extstat_clear;
+SELECT pg_catalog.pg_clear_extended_stats(
+ schemaname => 'stats_import',
+ relname => 'test',
+ statistics_schemaname => 'stats_import',
+ statistics_name => 'test_stat',
+ inherited => false);
+RESET ROLE;
+REVOKE MAINTAIN ON stats_import.test FROM regress_test_extstat_clear;
+REVOKE ALL ON SCHEMA stats_import FROM regress_test_extstat_clear;
+DROP ROLE regress_test_extstat_clear;
+
DROP SCHEMA stats_import CASCADE;