Thread

  1. Inconsistency in owner assignment between INDEX and STATISTICS

    JoongHyuk Shin <sjh910805@gmail.com> — 2026-02-14T08:48:11Z

    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.
    
  2. Re: Inconsistency in owner assignment between INDEX and STATISTICS

    JoongHyuk Shin <sjh910805@gmail.com> — 2026-02-26T09:52:38Z

    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.
    >
    
  3. Re: Inconsistency in owner assignment between INDEX and STATISTICS

    JoongHyuk Shin <sjh910805@gmail.com> — 2026-03-09T11:02:56Z

    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:
    
    > 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.
    >>
    >
    
  4. Re: Inconsistency in owner assignment between INDEX and STATISTICS

    Amit Khandekar <amitdkhan.pg@gmail.com> — 2026-03-10T14:07:10Z

    On Sat, 14 Feb 2026 at 14:18, 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()).
    
    I will try to divide the problem into two questions:
    
    1. Should the statistics object's owner be permanently associated with
    the table owner?
    
    From the docs, it does look like the current behaviour is intentional.
    
    https://www.postgresql.org/docs/current/sql-createstatistics.html :
    "You must be the owner of a table to create a statistics object
    reading it. Once created, however, the ownership of the statistics
    object is independent of the underlying table(s)."
    
    So I think we should not change the behaviour where the statistics
    object is created with independent ownership.
    
    With indexes, the behaviour has always been that it is associated with
    the table:
    
    postgres=# alter INDEX shared_schema.idx_bob owner to bob1;
    WARNING:  cannot change owner of index "idx_bob"
    HINT:  Change the ownership of the index's table instead.
    
    2. Regardless of that, should the "create statistics" create the stat
    object with the same ownership as the table's, if it's the superuser
    who is creating the statistics?
    
    I think, since there is no permanent association of ownership between
    the table and the statistics, it makes sense for the user who is
    running the create command to own the statistics, regardless of who
    the user is, provided that the user has privileges.
    
    >
    > 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).
    
    The permission error should be emitted if the DROP is on the right
    schema. See below.
    
    > 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).
    
    Maybe, make sure the table owner (and not the superuser) is creating
    the statistics?
    
    >
    > 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
    
    The statistics object is created in the default schema, not the table's schema.
    postgres=>   DROP STATISTICS public.stat_bob;
    ERROR:  must be owner of statistics object public.stat_bob
    
    Thanks
    -Amit Khandekar
    
    
    
    
  5. Re: Inconsistency in owner assignment between INDEX and STATISTICS

    JoongHyuk Shin <sjh910805@gmail.com> — 2026-03-11T09:54:01Z

    Thank you for the detailed feedback, Amit.
    
    You're right on both points. I had been comparing STATISTICS against INDEX
    and treating the difference as an inconsistency, but as you point out,
    INDEX ownership is special — it's tied to the table and intentionally not
    user-adjustable. STATISTICS follows the same ownership model as VIEW (the
    creator becomes the owner), which is consistent and by design.
    
    I also verified locally that my reproduction script was flawed: the
    "must be owner" error was caused by a schema search path issue, not an
    ownership restriction. The script did not demonstrate what I claimed.
    
    I'm withdrawing this proposal. Thanks again for taking the time to review
    it.
    
    On Tue, Mar 10, 2026 at 11:07 PM Amit Khandekar <amitdkhan.pg@gmail.com>
    wrote:
    
    > On Sat, 14 Feb 2026 at 14:18, 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()).
    >
    > I will try to divide the problem into two questions:
    >
    > 1. Should the statistics object's owner be permanently associated with
    > the table owner?
    >
    > From the docs, it does look like the current behaviour is intentional.
    >
    > https://www.postgresql.org/docs/current/sql-createstatistics.html :
    > "You must be the owner of a table to create a statistics object
    > reading it. Once created, however, the ownership of the statistics
    > object is independent of the underlying table(s)."
    >
    > So I think we should not change the behaviour where the statistics
    > object is created with independent ownership.
    >
    > With indexes, the behaviour has always been that it is associated with
    > the table:
    >
    > postgres=# alter INDEX shared_schema.idx_bob owner to bob1;
    > WARNING:  cannot change owner of index "idx_bob"
    > HINT:  Change the ownership of the index's table instead.
    >
    > 2. Regardless of that, should the "create statistics" create the stat
    > object with the same ownership as the table's, if it's the superuser
    > who is creating the statistics?
    >
    > I think, since there is no permanent association of ownership between
    > the table and the statistics, it makes sense for the user who is
    > running the create command to own the statistics, regardless of who
    > the user is, provided that the user has privileges.
    >
    > >
    > > 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).
    >
    > The permission error should be emitted if the DROP is on the right
    > schema. See below.
    >
    > > 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).
    >
    > Maybe, make sure the table owner (and not the superuser) is creating
    > the statistics?
    >
    > >
    > > 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
    >
    > The statistics object is created in the default schema, not the table's
    > schema.
    > postgres=>   DROP STATISTICS public.stat_bob;
    > ERROR:  must be owner of statistics object public.stat_bob
    >
    > Thanks
    > -Amit Khandekar
    >
    
  6. Re: Inconsistency in owner assignment between INDEX and STATISTICS

    Tom Lane <tgl@sss.pgh.pa.us> — 2026-03-15T20:09:39Z

    Shin Berg <sjh910805@gmail.com> writes:
    > Thank you for the detailed feedback, Amit.
    > You're right on both points. I had been comparing STATISTICS against INDEX
    > and treating the difference as an inconsistency, but as you point out,
    > INDEX ownership is special — it's tied to the table and intentionally not
    > user-adjustable. STATISTICS follows the same ownership model as VIEW (the
    > creator becomes the owner), which is consistent and by design.
    
    One point that was not mentioned is that while indexes are necessarily
    tied to a single table, statistics objects might not always be.  The
    long-term hope is to allow statistics on cross-table combinations of
    columns, which is why the syntax was intentionally set up to look like
    SELECT.  So, just like views, it's reasonable to give them independent
    ownership.
    
    			regards, tom lane
    
    
    
    
  7. Re: Inconsistency in owner assignment between INDEX and STATISTICS

    JoongHyuk Shin <sjh910805@gmail.com> — 2026-03-16T01:15:24Z

    Thank you for the additional context, Tom. That makes the design intent
    much clearer.
    Cross-table statistics, if realized, would be a significant improvement for
    join cardinality estimation; looking forward to seeing that develop.
    
    Regards,
    Joshua Shin
    
    On Mon, Mar 16, 2026 at 5:09 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    
    > Shin Berg <sjh910805@gmail.com> writes:
    > > Thank you for the detailed feedback, Amit.
    > > You're right on both points. I had been comparing STATISTICS against
    > INDEX
    > > and treating the difference as an inconsistency, but as you point out,
    > > INDEX ownership is special — it's tied to the table and intentionally not
    > > user-adjustable. STATISTICS follows the same ownership model as VIEW (the
    > > creator becomes the owner), which is consistent and by design.
    >
    > One point that was not mentioned is that while indexes are necessarily
    > tied to a single table, statistics objects might not always be.  The
    > long-term hope is to allow statistics on cross-table combinations of
    > columns, which is why the syntax was intentionally set up to look like
    > SELECT.  So, just like views, it's reasonable to give them independent
    > ownership.
    >
    >                         regards, tom lane
    >
    
  8. Re: Inconsistency in owner assignment between INDEX and STATISTICS

    JoongHyuk Shin <sjh910805@gmail.com> — 2026-04-07T10:34:54Z

    One related thing I noticed.
    Unlike CREATE INDEX, CREATE STATISTICS on a partitioned table does not
    propagate to child partitions.
    Is that intentional, or simply not implemented yet?
    
    In practice, if you create statistics on a partitioned parent and then
    EXPLAIN a WHERE query,
    the planner doesn't use the stats because after partition pruning it looks
    at the child's statistics,
    where none exist. You have to create statistics on each child explicitly to
    get the benefit.
    
    If the answer is "create stats per partition yourself", that's fine.
    Just wanted to confirm whether this is by design.
    
    On Mon, Mar 16, 2026 at 10:15 AM Shin Berg <sjh910805@gmail.com> wrote:
    
    > Thank you for the additional context, Tom. That makes the design intent
    > much clearer.
    > Cross-table statistics, if realized, would be a significant improvement
    > for join cardinality estimation; looking forward to seeing that develop.
    >
    > Regards,
    > Joshua Shin
    >
    > On Mon, Mar 16, 2026 at 5:09 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    >
    >> Shin Berg <sjh910805@gmail.com> writes:
    >> > Thank you for the detailed feedback, Amit.
    >> > You're right on both points. I had been comparing STATISTICS against
    >> INDEX
    >> > and treating the difference as an inconsistency, but as you point out,
    >> > INDEX ownership is special — it's tied to the table and intentionally
    >> not
    >> > user-adjustable. STATISTICS follows the same ownership model as VIEW
    >> (the
    >> > creator becomes the owner), which is consistent and by design.
    >>
    >> One point that was not mentioned is that while indexes are necessarily
    >> tied to a single table, statistics objects might not always be.  The
    >> long-term hope is to allow statistics on cross-table combinations of
    >> columns, which is why the syntax was intentionally set up to look like
    >> SELECT.  So, just like views, it's reasonable to give them independent
    >> ownership.
    >>
    >>                         regards, tom lane
    >>
    >