Hi,

Following up on my earlier proposal — I've gone ahead and written a patch
rather than waiting for feedback.

The fix is in CreateStatistics(): after opening the relation, stxowner is
set to rel->rd_rel->relowner instead of GetUserId().  The permission check
is left using GetUserId() so that only the relation owner (or a superuser)
can create statistics, but the ownership recorded in pg_statistic_ext now
matches what CREATE INDEX does.

A regression test is included in stats_ext.sql to verify that the
statistics owner equals the table owner when a superuser creates the
statistics object.

Patch attached.

Thanks,
Joshua-Shin

On Thu, Feb 26, 2026 at 6:52 PM Shin Berg <[email protected]> wrote:

> Gentle ping on this thread — any thoughts or concerns about the
> proposed alignment?
>
> Thanks.
>
> On Sat, Feb 14, 2026 at 5:48 PM Shin Berg <[email protected]> wrote:
>
>> Hi,
>>
>> I'd like to raise a small consistency issue between how INDEX and
>> extended STATISTICS handle object ownership, and ask whether aligning them
>> would be desirable.
>>
>> Current behavior (tested on REL_17_STABLE):
>>
>> - When a superuser creates an INDEX on another user's table, the index is
>> owned by the *table owner* (see catalog/index.c: index relation's relowner
>> is set from the heap relation's relowner).
>> - When a superuser creates STATISTICS on another user's table, the
>> statistics object is owned by the *current user* (statscmds.c: stxowner =
>> GetUserId()).
>>
>> So in a scenario where a DBA creates both an index and extended
>> statistics on a user's table, the table owner can DROP the index (because
>> they own it) but cannot DROP the statistics object (they get "does not
>> exist" when lacking ownership, which hides the real permission issue). That
>> can cause operational friction in multi-tenant or shared-schema setups
>> (e.g. the table owner cannot drop the statistics to resolve dependency
>> issues before altering the table).
>>
>> Reproduction (as superuser, then as table owner):
>>
>>   CREATE SCHEMA shared_schema;
>>   CREATE USER bob;
>>   GRANT USAGE, CREATE ON SCHEMA shared_schema TO bob;
>>
>>   SET ROLE bob;
>>   CREATE TABLE shared_schema.bob_table (a int, b int);
>>   RESET ROLE;
>>
>>   CREATE INDEX idx_bob ON shared_schema.bob_table(a);
>>   CREATE STATISTICS stat_bob ON a, b FROM shared_schema.bob_table;
>>
>>   SELECT 'INDEX', c.relname, pg_get_userbyid(c.relowner) FROM pg_index i
>>     JOIN pg_class c ON c.oid = i.indexrelid
>>     WHERE indrelid = 'shared_schema.bob_table'::regclass
>>   UNION ALL
>>   SELECT 'STATISTICS', stxname, pg_get_userbyid(stxowner) FROM
>> pg_statistic_ext
>>     WHERE stxrelid = 'shared_schema.bob_table'::regclass;
>>   -- INDEX owner = bob, STATISTICS owner = superuser
>>
>>   SET ROLE bob;
>>   DROP INDEX shared_schema.idx_bob;        -- succeeds
>>   DROP STATISTICS shared_schema.stat_bob;  -- ERROR: statistics object
>> "..." does not exist
>>
>> I'm not sure if the current STATISTICS ownership behavior was
>> intentional. If it wasn't, would it make sense to assign the statistics
>> object's owner to the relation owner (same as INDEX) for consistency and to
>> avoid the above scenario?
>>
>> Thanks for your time.
>>
>

Attachment: 0001-Make-CREATE-STATISTICS-assign-ownership-to-the-relat.patch
Description: Binary data

Reply via email to