Thread: BUG #8695: Reloading dump fails at COMMENT ON EXTENSION plpgsql

BUG #8695: Reloading dump fails at COMMENT ON EXTENSION plpgsql

From
chris@chrullrich.net
Date:
The following bug has been logged on the website:

Bug reference:      8695
Logged by:          Christian Ullrich
Email address:      chris@chrullrich.net
PostgreSQL version: 9.3.2
Operating system:   all
Description:

A non-superuser cannot reload any dump of a database that contains the
plpgsql extension, because the dump unconditionally attempts to set the
comment on that extension. This fails because plpgsql is owned by the
superuser who installed it.


This contradicts the manual, which says: "The dumps produced by pg_dump are
relative to template0." The plpgsql extension is present in template0, with
the identical comment, and therefore neither extension nor comment should be
dumped at all. (I know this is splitting hairs, because pg_dump does not
actually compare the subject database to template0, but still, the
contradiction is there.)


The extension itself is dumped as CREATE IF NOT EXISTS, so that works, but
there is no conditional syntax for comments, and since pg_dump does not know
whether the comment has been changed from the default, it could not use one
anyway.


I can think of one possible fix (aside from simply filtering that line from
the dump): COMMENT could be a no-op if the requested comment is identical to
the existing one.


Another idea I had was to allow comments to be part of an extension, so that
pg_dump would not dump them, but that does not work because pg_dump does not
know if a comment has been changed from the original value. Not that anyone
would ever do that.

Re: BUG #8695: Reloading dump fails at COMMENT ON EXTENSION plpgsql

From
Bruce Momjian
Date:
On Sun, Dec 22, 2013 at 01:56:13AM +0000, chris@chrullrich.net wrote:
> The following bug has been logged on the website:
>
> Bug reference:      8695
> Logged by:          Christian Ullrich
> Email address:      chris@chrullrich.net
> PostgreSQL version: 9.3.2
> Operating system:   all
> Description:
>
> A non-superuser cannot reload any dump of a database that contains the
> plpgsql extension, because the dump unconditionally attempts to set the
> comment on that extension. This fails because plpgsql is owned by the
> superuser who installed it.
>
>
> This contradicts the manual, which says: "The dumps produced by pg_dump are
> relative to template0." The plpgsql extension is present in template0, with
> the identical comment, and therefore neither extension nor comment should be
> dumped at all. (I know this is splitting hairs, because pg_dump does not
> actually compare the subject database to template0, but still, the
> contradiction is there.)
>
>
> The extension itself is dumped as CREATE IF NOT EXISTS, so that works, but
> there is no conditional syntax for comments, and since pg_dump does not know
> whether the comment has been changed from the default, it could not use one
> anyway.
>
>
> I can think of one possible fix (aside from simply filtering that line from
> the dump): COMMENT could be a no-op if the requested comment is identical to
> the existing one.
>
>
> Another idea I had was to allow comments to be part of an extension, so that
> pg_dump would not dump them, but that does not work because pg_dump does not
> know if a comment has been changed from the original value. Not that anyone
> would ever do that.

I can reproduce this bug:

    $ psql test
    psql (9.4devel)
    Type "help" for help.

    test=> CREATE USER joe;
    CREATE ROLE
    test=> CREATE DATABASE test2 OWNER joe;
    CREATE DATABASE
    test=> \q
    $ pg_dump test | psql -e -U joe test2
    SET statement_timeout = 0;
    SET
    SET lock_timeout = 0;
    SET
    SET client_encoding = 'UTF8';
    SET
    SET standard_conforming_strings = on;
    SET
    SET check_function_bodies = false;
    SET
    SET client_min_messages = warning;
    SET
    CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
    CREATE EXTENSION
-->    COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
-->    ERROR:  must be owner of extension plpgsql
    REVOKE ALL ON SCHEMA public FROM PUBLIC;
    WARNING:  no privileges could be revoked for "public"
    REVOKE
    REVOKE ALL ON SCHEMA public FROM postgres;
    WARNING:  no privileges could be revoked for "public"
    REVOKE
    GRANT ALL ON SCHEMA public TO postgres;
    WARNING:  no privileges were granted for "public"
    GRANT
    GRANT ALL ON SCHEMA public TO PUBLIC;
    WARNING:  no privileges were granted for "public"
    GRANT

This would certainly cause a restore to abort for a non-super-user if
psql used --set ON_ERROR_STOP=on.  Any easy way to fix this?  I am not
super-excited about the suggested fixes listed above.

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + Everyone has their own god. +

Re: BUG #8695: Reloading dump fails at COMMENT ON EXTENSION plpgsql

From
Christian Ullrich
Date:
* Bruce Momjian wrote:

> On Sun, Dec 22, 2013 at 01:56:13AM +0000, chris@chrullrich.net wrote:

>> A non-superuser cannot reload any dump of a database that contains the
>> plpgsql extension, because the dump unconditionally attempts to set the
>> comment on that extension. This fails because plpgsql is owned by the
>> superuser who installed it.

>> I can think of one possible fix (aside from simply filtering that line from
>> the dump): COMMENT could be a no-op if the requested comment is identical to
>> the existing one.
>>
>> Another idea I had was to allow comments to be part of an extension, so that
>> pg_dump would not dump them, but that does not work because pg_dump does not
>> know if a comment has been changed from the original value. Not that anyone
>> would ever do that.

> This would certainly cause a restore to abort for a non-super-user if
> psql used --set ON_ERROR_STOP=on.  Any easy way to fix this?  I am not
> super-excited about the suggested fixes listed above.

Not just with ON_ERROR_STOP, but also with --single-transaction. IMO,
that is even worse, because restoring that way is a) a lot faster for
non-enormous databases, and b) easier to clean up if some other error
happens.

Warning: Raw brain dump following.

As for possible fixes, you can find an argument in favor of my no-op
suggestion below, along with some more ideas that probably won't work,
but the root cause is the "relative to template0" thing. As long as
pg_dump does not adhere to that, the problem remains. It is impossible
to make it adhere without expending large amounts of work both on
development and at runtime, so that statement should at least be changed
to mention that comments on extensions are included in the dump.


Other than that, I think my suggestion of letting COMMENT succeed (fail
silently, actually) if the comment would not change even if the command
were executed is not that bad. If the command is -- in the specific
situation -- idempotent, why not simply skip it?

(The only situation where that might be a problem is if someone made
pg_description not readable to everyone, thereby breaking all the \d..+
psql commands for nonprivileged users. In that case, such a user could
possibly probe for existing comments, whatever good that would do them.
It's at least imaginable that some application might abuse the comments
for configuration metadata. Of course, COMMENT would have to read the
existing comment, thereby failing early without leaking.)


The simplest fix I can think of on further consideration is to give the
database owner blanket COMMENT privilege on everything. Without checking
the archives, I suspect that idea was considered and rejected when
comments were introduced, because it looks rather obvious.

Another idea: Per-user comments. If the user invoking COMMENT does not
have the required privileges to set the global comment, a per-user
comment would be set instead, closely followed by chaos and confusion.

Yet another: COMMENT IF NOT EXISTS, without looking at the existing
comment. Nicely self-contained, but the semantics if there is no comment
yet look questionable: Loading a dump with a comment for an existing
object that does not have one in the target database would fail again,
just as it does now. That might be considered correct because we're
trying to set a comment where none was before, but it does not pass the
"relative to template0" test either if the comment existed in there and
was removed in the dumped database.

And one more: Extension ownership could implicitly follow database
ownership. Not backward compatible; would give database owners
additional privileges simply by upgrading, and would obsolete the
pg_extension.extowner column.

--
Christian

Re: BUG #8695: Reloading dump fails at COMMENT ON EXTENSION plpgsql

From
Bruce Momjian
Date:
On Mon, Mar 31, 2014 at 01:00:09PM -0400, Bruce Momjian wrote:
>     CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
>     CREATE EXTENSION
> -->    COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
> -->    ERROR:  must be owner of extension plpgsql
>     REVOKE ALL ON SCHEMA public FROM PUBLIC;
>     WARNING:  no privileges could be revoked for "public"
>     REVOKE
>     REVOKE ALL ON SCHEMA public FROM postgres;
>     WARNING:  no privileges could be revoked for "public"
>     REVOKE
>     GRANT ALL ON SCHEMA public TO postgres;
>     WARNING:  no privileges were granted for "public"
>     GRANT
>     GRANT ALL ON SCHEMA public TO PUBLIC;
>     WARNING:  no privileges were granted for "public"
>     GRANT
>
> This would certainly cause a restore to abort for a non-super-user if
> psql used --set ON_ERROR_STOP=on.  Any easy way to fix this?  I am not
> super-excited about the suggested fixes listed above.

In looking at this further, I wonder why we don't filter out pg_catalog
extensions when we dump them;  right now we use:

    appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
                         "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
                         "FROM pg_extension x "
                         "JOIN pg_namespace n ON n.oid = x.extnamespace");

I see a filter on pg_catalog for pg_proc.  Is there a reason there isn't
one for pg_extension?

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + Everyone has their own god. +

Re: BUG #8695: Reloading dump fails at COMMENT ON EXTENSION plpgsql

From
Tom Lane
Date:
Bruce Momjian <bruce@momjian.us> writes:
> I see a filter on pg_catalog for pg_proc.  Is there a reason there isn't
> one for pg_extension?

Extensions don't live in schemas.

            regards, tom lane

Re: BUG #8695: Reloading dump fails at COMMENT ON EXTENSION plpgsql

From
Bruce Momjian
Date:
On Wed, Apr  9, 2014 at 10:15:28PM -0400, Tom Lane wrote:
> Bruce Momjian <bruce@momjian.us> writes:
> > I see a filter on pg_catalog for pg_proc.  Is there a reason there isn't
> > one for pg_extension?
>
> Extensions don't live in schemas.

That's what I thought too, but I see a schema file in pg_extensions:

    test=> \d pg_extension
       Table "pg_catalog.pg_extension"
         Column     |  Type   | Modifiers
    ----------------+---------+-----------
     extname        | name    | not null
     extowner       | oid     | not null
-->     extnamespace   | oid     | not null
     extrelocatable | boolean | not null
     extversion     | text    |
     extconfig      | oid[]   |
     extcondition   | text[]  |
    Indexes:
        "pg_extension_name_index" UNIQUE, btree (extname)
        "pg_extension_oid_index" UNIQUE, btree (oid)

    SELECT extname, extnamespace, nspname
    FROM pg_extension, pg_namespace
    WHERE extnamespace = pg_namespace.oid;
     extname | extnamespace |  nspname
    ---------+--------------+------------
     plpgsql |           11 | pg_catalog
    (1 row)

Should we hard-code a pg_catalog plpgsql to be skipped in pg_dump?

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + Everyone has their own god. +

Re: BUG #8695: Reloading dump fails at COMMENT ON EXTENSION plpgsql

From
Tom Lane
Date:
Bruce Momjian <bruce@momjian.us> writes:
> That's what I thought too, but I see a schema file in pg_extensions:

Read the manual.

> Should we hard-code a pg_catalog plpgsql to be skipped in pg_dump?

No, I don't think so.

The real issue here is that we don't have a notion of a "built-in
extension".  I think this was specifically debated back when we
extension-ified plpgsql, though I don't recall details of why
we ended up not doing that.  Maybe the idea was that you could
drop and then re-add plpgsql?  Anyway, I think this is not such
a simple issue and a one-line hack in pg_dump is not likely to
improve matters.

            regards, tom lane

Re: BUG #8695: Reloading dump fails at COMMENT ON EXTENSION plpgsql

From
Bruce Momjian
Date:
On Wed, Apr  9, 2014 at 11:13:57PM -0400, Tom Lane wrote:
> Bruce Momjian <bruce@momjian.us> writes:
> > That's what I thought too, but I see a schema file in pg_extensions:
>
> Read the manual.
>
> > Should we hard-code a pg_catalog plpgsql to be skipped in pg_dump?
>
> No, I don't think so.
>
> The real issue here is that we don't have a notion of a "built-in
> extension".  I think this was specifically debated back when we
> extension-ified plpgsql, though I don't recall details of why
> we ended up not doing that.  Maybe the idea was that you could
> drop and then re-add plpgsql?  Anyway, I think this is not such
> a simple issue and a one-line hack in pg_dump is not likely to
> improve matters.

OK, I added a TODO:

    Prevent PL/pgSQL comment from throwing an error in a
    non-superuser restore

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + Everyone has their own god. +