Commit df5d9bb8 authored by Tom Lane's avatar Tom Lane

Allow pg_dump to dump non-extension members of an extension-owned schema.

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>
parent e97e9c57
......@@ -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);
}
/*
......
......@@ -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
......
......@@ -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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment