Inconsistency in owner assignment between INDEX and STATISTICS

Started by Shin Berg24 days ago3 messages
Jump to latest
#1Shin Berg
sjh910805@gmail.com

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.

#2Shin Berg
sjh910805@gmail.com
In reply to: Shin Berg (#1)
Re: Inconsistency in owner assignment between INDEX and STATISTICS

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 <sjh910805@gmail.com> wrote:

Show quoted text

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.

#3Shin Berg
sjh910805@gmail.com
In reply to: Shin Berg (#2)
Re: Inconsistency in owner assignment between INDEX and STATISTICS

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 <sjh910805@gmail.com> wrote:

Show quoted text

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 <sjh910805@gmail.com> 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.

Attachments:

0001-Make-CREATE-STATISTICS-assign-ownership-to-the-relat.patchapplication/octet-stream; name=0001-Make-CREATE-STATISTICS-assign-ownership-to-the-relat.patchDownload+26-2