]> granicus.if.org Git - postgresql/commitdiff
Allow pg_dump to dump non-extension members of an extension-owned schema.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 8 Sep 2016 17:12:01 +0000 (13:12 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 8 Sep 2016 17:12:01 +0000 (13:12 -0400)
Previously, if a schema was created by an extension, a normal pg_dump run
(not --binary-upgrade) would summarily skip every object in that schema.
In a case where an extension creates a schema and then users create other
objects within that schema, this does the wrong thing: we want pg_dump
to skip the schema but still create the non-extension-owned objects.

There's no easy way to fix this pre-9.6, because in earlier versions the
"dump" status for a schema is just a bool and there's no way to distinguish
"dump me" from "dump my members".  However, as of 9.6 we do have enough
state to represent that, so this is a simple correction of the logic in
selectDumpableNamespace.

In passing, make some cosmetic fixes in nearby code.

Martín Marqués, reviewed by Michael Paquier

Discussion: <99581032-71de-6466-c325-069861f1947d@2ndquadrant.com>

src/bin/pg_dump/pg_dump.c
src/test/modules/test_pg_dump/t/001_base.pl
src/test/modules/test_pg_dump/test_pg_dump--1.0.sql

index 165200f0fc47a7fb729355d240b5047af0243123..ba9c2765938336dc0f40a63804e72fc24a8467d8 100644 (file)
@@ -1302,7 +1302,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 
        /*
         * In 9.6 and above, mark the member object to have any non-initial ACL,
-        * policies, and security lables dumped.
+        * policies, and security labels dumped.
         *
         * Note that any initial ACLs (see pg_init_privs) will be removed when we
         * extract the information about the object.  We don't provide support for
@@ -1324,8 +1324,8 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
                        dobj->dump = DUMP_COMPONENT_NONE;
                else
                        dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL |
-                                                       DUMP_COMPONENT_SECLABEL | DUMP_COMPONENT_POLICY);
-
+                                                                                                       DUMP_COMPONENT_SECLABEL |
+                                                                                                       DUMP_COMPONENT_POLICY);
        }
 
        return true;
@@ -1338,15 +1338,11 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 static void
 selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 {
-       if (checkExtensionMembership(&nsinfo->dobj, fout))
-               return;                                 /* extension membership overrides all else */
-
        /*
         * If specific tables are being dumped, do not dump any complete
         * namespaces. If specific namespaces are being dumped, dump just those
         * namespaces. Otherwise, dump all non-system namespaces.
         */
-
        if (table_include_oids.head != NULL)
                nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
        else if (schema_include_oids.head != NULL)
@@ -1355,18 +1351,21 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
                                                                   nsinfo->dobj.catId.oid) ?
                        DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
        else if (fout->remoteVersion >= 90600 &&
-                        strncmp(nsinfo->dobj.name, "pg_catalog",
-                                        strlen("pg_catalog")) == 0)
-
+                        strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
+       {
                /*
                 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
                 * they are interesting (and not the original ACLs which were set at
                 * initdb time, see pg_init_privs).
                 */
                nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
+       }
        else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
                         strcmp(nsinfo->dobj.name, "information_schema") == 0)
+       {
+               /* Other system schemas don't get dumped */
                nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
+       }
        else
                nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
 
@@ -1377,6 +1376,15 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
                simple_oid_list_member(&schema_exclude_oids,
                                                           nsinfo->dobj.catId.oid))
                nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
+
+       /*
+        * If the schema belongs to an extension, allow extension membership to
+        * override the dump decision for the schema itself.  However, this does
+        * not change dump_contains, so this won't change what we do with objects
+        * within the schema.  (If they belong to the extension, they'll get
+        * suppressed by it, otherwise not.)
+        */
+       (void) checkExtensionMembership(&nsinfo->dobj, fout);
 }
 
 /*
index f02beb3d9c481cf27b295d885a98a18076ed6978..55f0eb45474cf244781622352a7e4a0204d17b85 100644 (file)
@@ -429,7 +429,211 @@ my %tests = (
                unlike => {
                        no_privs           => 1,
                        pg_dumpall_globals => 1,
-                       section_post_data  => 1, }, },);
+                       section_post_data  => 1, }, },
+       # Objects included in extension part of a schema created by this extension */
+       'CREATE TABLE regress_pg_dump_schema.test_table' => {
+               regexp => qr/^
+                       \QCREATE TABLE test_table (\E
+                       \n\s+\Qcol1 integer,\E
+                       \n\s+\Qcol2 integer\E
+                       \n\);$/xm,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_privs           => 1,
+                       no_owner           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       'GRANT SELECT ON regress_pg_dump_schema.test_table' => {
+               regexp => qr/^
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\E\n
+                       \QGRANT SELECT ON TABLE test_table TO regress_dump_test_role;\E\n
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\E
+                       $/xms,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_owner           => 1,
+                       no_privs           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       'CREATE SEQUENCE regress_pg_dump_schema.test_seq' => {
+               regexp => qr/^
+                    \QCREATE SEQUENCE test_seq\E
+                    \n\s+\QSTART WITH 1\E
+                    \n\s+\QINCREMENT BY 1\E
+                    \n\s+\QNO MINVALUE\E
+                    \n\s+\QNO MAXVALUE\E
+                    \n\s+\QCACHE 1;\E
+                    $/xm,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_privs           => 1,
+                       no_owner           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       'GRANT USAGE ON regress_pg_dump_schema.test_seq' => {
+               regexp => qr/^
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\E\n
+                       \QGRANT USAGE ON SEQUENCE test_seq TO regress_dump_test_role;\E\n
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\E
+                       $/xms,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_owner           => 1,
+                       no_privs           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       'CREATE TYPE regress_pg_dump_schema.test_type' => {
+               regexp => qr/^
+                    \QCREATE TYPE test_type AS (\E
+                    \n\s+\Qcol1 integer\E
+                    \n\);$/xm,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_privs           => 1,
+                       no_owner           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       'GRANT USAGE ON regress_pg_dump_schema.test_type' => {
+               regexp => qr/^
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\E\n
+                       \QGRANT ALL ON TYPE test_type TO regress_dump_test_role;\E\n
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\E
+                       $/xms,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_owner           => 1,
+                       no_privs           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       'CREATE FUNCTION regress_pg_dump_schema.test_func' => {
+               regexp => qr/^
+            \QCREATE FUNCTION test_func() RETURNS integer\E
+            \n\s+\QLANGUAGE sql\E
+            $/xm,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_privs           => 1,
+                       no_owner           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       'GRANT ALL ON regress_pg_dump_schema.test_func' => {
+               regexp => qr/^
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\E\n
+                       \QGRANT ALL ON FUNCTION test_func() TO regress_dump_test_role;\E\n
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\E
+                       $/xms,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_owner           => 1,
+                       no_privs           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       'CREATE AGGREGATE regress_pg_dump_schema.test_agg' => {
+               regexp => qr/^
+            \QCREATE AGGREGATE test_agg(smallint) (\E
+            \n\s+\QSFUNC = int2_sum,\E
+            \n\s+\QSTYPE = bigint\E
+            \n\);$/xm,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_privs           => 1,
+                       no_owner           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       'GRANT ALL ON regress_pg_dump_schema.test_agg' => {
+               regexp => qr/^
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\E\n
+                       \QGRANT ALL ON FUNCTION test_agg(smallint) TO regress_dump_test_role;\E\n
+                       \QSELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\E
+                       $/xms,
+               like   => { binary_upgrade => 1, },
+               unlike => {
+                       clean              => 1,
+                       clean_if_exists    => 1,
+                       createdb           => 1,
+                       defaults           => 1,
+                       no_owner           => 1,
+                       no_privs           => 1,
+                       pg_dumpall_globals => 1,
+                       schema_only        => 1,
+                       section_pre_data   => 1,
+                       section_post_data  => 1, }, },
+       # Objects not included in extension, part of schema created by extension
+       'CREATE TABLE regress_pg_dump_schema.external_tab' => {
+               create_order => 4,
+               create_sql   => 'CREATE TABLE regress_pg_dump_schema.external_tab
+                                                  (col1 int);',
+               regexp => qr/^
+                       \QCREATE TABLE external_tab (\E
+                       \n\s+\Qcol1 integer\E
+                       \n\);$/xm,
+               like => {
+                       binary_upgrade   => 1,
+                       clean            => 1,
+                       clean_if_exists  => 1,
+                       createdb         => 1,
+                       defaults         => 1,
+                       no_owner         => 1,
+                       no_privs           => 1,
+                       schema_only      => 1,
+                       section_pre_data => 1, },
+               unlike => {
+                       pg_dumpall_globals => 1,
+                       section_post_data  => 1, }, }, );
 
 #########################################
 # Create a PG instance to test actually dumping from
index c2fe90d5abcd9e849d41cbd564c32dbc64318697..ca9fb18e5408e466c268aaea3ac7ce1d88fa3eb3 100644 (file)
@@ -10,6 +10,8 @@ CREATE TABLE regress_pg_dump_table (
 
 CREATE SEQUENCE regress_pg_dump_seq;
 
+CREATE SCHEMA regress_pg_dump_schema;
+
 GRANT USAGE ON regress_pg_dump_seq TO regress_dump_test_role;
 
 GRANT SELECT ON regress_pg_dump_table TO regress_dump_test_role;
@@ -19,3 +21,25 @@ GRANT SELECT(col2) ON regress_pg_dump_table TO regress_dump_test_role;
 REVOKE SELECT(col2) ON regress_pg_dump_table FROM regress_dump_test_role;
 
 CREATE ACCESS METHOD regress_test_am TYPE INDEX HANDLER bthandler;
+
+-- Create a set of objects that are part of the schema created by
+-- this extension.
+CREATE TABLE regress_pg_dump_schema.test_table (
+       col1 int,
+       col2 int
+);
+GRANT SELECT ON regress_pg_dump_schema.test_table TO regress_dump_test_role;
+
+CREATE SEQUENCE regress_pg_dump_schema.test_seq;
+GRANT USAGE ON regress_pg_dump_schema.test_seq TO regress_dump_test_role;
+
+CREATE TYPE regress_pg_dump_schema.test_type AS (col1 int);
+GRANT USAGE ON TYPE regress_pg_dump_schema.test_type TO regress_dump_test_role;
+
+CREATE FUNCTION regress_pg_dump_schema.test_func () RETURNS int
+AS 'SELECT 1;' LANGUAGE SQL;
+GRANT EXECUTE ON FUNCTION regress_pg_dump_schema.test_func() TO regress_dump_test_role;
+
+CREATE AGGREGATE regress_pg_dump_schema.test_agg(int2)
+(SFUNC = int2_sum, STYPE = int8);
+GRANT EXECUTE ON FUNCTION regress_pg_dump_schema.test_agg(int2) TO regress_dump_test_role;