Commit 5d589993 authored by Stephen Frost's avatar Stephen Frost

pg_dump performance and other fixes

Do not try to dump objects which do not have ACLs when only ACLs are
being requested.  This results in a significant performance improvement
as we can avoid querying for further information on these objects when
we don't need to.

When limiting the components to dump for an extension, consider what
components have been requested.  Initially, we incorrectly hard-coded
the components of the extension objects to dump, which would mean that
we wouldn't dump some components even with they were asked for and in
other cases we would dump components which weren't requested.

Correct defaultACLs to use 'dump_contains' instead of 'dump'.  The
defaultACL is considered a member of the namespace and should be
dumped based on the same set of components that the other objects in
the schema are, not based on what we're dumping for the namespace
itself (which might not include ACLs, if the namespace has just the
default or initial ACL).

Use DUMP_COMPONENT_ACL for from-initdb objects, to allow users to
change their ACLs, should they wish to.  This just extends what we
are doing for the pg_catalog namespace to objects which are not
members of namespaces.

Due to column ACLs being treated a bit differently from other ACLs
(they are actually reset to NULL when all privileges are revoked),
adjust the query which gathers column-level ACLs to consider all of
the ACL-relevant columns.
parent 64d60c8b
......@@ -1297,14 +1297,17 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
* contents rather than replace the extension contents with something
* different.
*/
if (!fout->dopt->binary_upgrade && fout->remoteVersion >= 90600)
dobj->dump = DUMP_COMPONENT_ACL |
DUMP_COMPONENT_SECLABEL |
DUMP_COMPONENT_POLICY;
else if (!fout->dopt->binary_upgrade)
dobj->dump = DUMP_COMPONENT_NONE;
else
if (fout->dopt->binary_upgrade)
dobj->dump = ext->dobj.dump;
else
{
if (fout->remoteVersion < 90600)
dobj->dump = DUMP_COMPONENT_NONE;
else
dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL |
DUMP_COMPONENT_SECLABEL | DUMP_COMPONENT_POLICY);
}
return true;
}
......@@ -1452,7 +1455,7 @@ selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
if (dinfo->dobj.namespace)
/* default ACLs are considered part of the namespace */
dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
else
dinfo->dobj.dump = dopt->include_everything ?
DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
......@@ -1473,6 +1476,10 @@ selectDumpableCast(CastInfo *cast, Archive *fout)
if (checkExtensionMembership(&cast->dobj, fout))
return; /* extension membership overrides all else */
/*
* This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
* support ACLs currently.
*/
if (cast->dobj.catId.oid < (Oid) FirstNormalObjectId)
cast->dobj.dump = DUMP_COMPONENT_NONE;
else
......@@ -1494,11 +1501,23 @@ selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
if (checkExtensionMembership(&plang->dobj, fout))
return; /* extension membership overrides all else */
if (plang->dobj.catId.oid < (Oid) FirstNormalObjectId)
/*
* Only include procedural languages when we are dumping everything.
*
* For from-initdb procedural languages, only include ACLs, as we do for
* the pg_catalog namespace. We need this because procedural languages do
* not live in any namespace.
*/
if (!fout->dopt->include_everything)
plang->dobj.dump = DUMP_COMPONENT_NONE;
else
plang->dobj.dump = fout->dopt->include_everything ?
DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
{
if (plang->dobj.catId.oid < (Oid) FirstNormalObjectId)
plang->dobj.dump = fout->remoteVersion < 90600 ?
DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
else
plang->dobj.dump = DUMP_COMPONENT_ALL;
}
}
/*
......@@ -1515,6 +1534,10 @@ selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
if (checkExtensionMembership(&method->dobj, fout))
return; /* extension membership overrides all else */
/*
* This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
* they do not support ACLs currently.
*/
if (method->dobj.catId.oid < (Oid) FirstNormalObjectId)
method->dobj.dump = DUMP_COMPONENT_NONE;
else
......@@ -1535,11 +1558,17 @@ selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
static void
selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
{
/*
* Use DUMP_COMPONENT_ACL for from-initdb extensions, to allow users
* to change permissions on those objects, if they wish to, and have
* those changes preserved.
*/
if (dopt->binary_upgrade && extinfo->dobj.catId.oid < (Oid) FirstNormalObjectId)
extinfo->dobj.dump = DUMP_COMPONENT_NONE;
extinfo->dobj.dump = DUMP_COMPONENT_ACL;
else
extinfo->dobj.dump = dopt->include_everything ?
DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
extinfo->dobj.dump = extinfo->dobj.dump_contains =
dopt->include_everything ? DUMP_COMPONENT_ALL :
DUMP_COMPONENT_NONE;
}
/*
......@@ -4175,6 +4204,9 @@ getOperators(Archive *fout, int *numOprs)
/* Decide whether we want to dump it */
selectDumpableObject(&(oprinfo[i].dobj), fout);
/* Operators do not currently have ACLs. */
oprinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
if (strlen(oprinfo[i].rolname) == 0)
write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
oprinfo[i].dobj.name);
......@@ -4259,6 +4291,9 @@ getCollations(Archive *fout, int *numCollations)
/* Decide whether we want to dump it */
selectDumpableObject(&(collinfo[i].dobj), fout);
/* Collations do not currently have ACLs. */
collinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
}
PQclear(res);
......@@ -4340,6 +4375,9 @@ getConversions(Archive *fout, int *numConversions)
/* Decide whether we want to dump it */
selectDumpableObject(&(convinfo[i].dobj), fout);
/* Conversions do not currently have ACLs. */
convinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
}
PQclear(res);
......@@ -4413,6 +4451,9 @@ getAccessMethods(Archive *fout, int *numAccessMethods)
/* Decide whether we want to dump it */
selectDumpableAccessMethod(&(aminfo[i]), fout);
/* Access methods do not currently have ACLs. */
aminfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
}
PQclear(res);
......@@ -4506,6 +4547,9 @@ getOpclasses(Archive *fout, int *numOpclasses)
/* Decide whether we want to dump it */
selectDumpableObject(&(opcinfo[i].dobj), fout);
/* Op Classes do not currently have ACLs. */
opcinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
if (fout->remoteVersion >= 70300)
{
if (strlen(opcinfo[i].rolname) == 0)
......@@ -4594,6 +4638,9 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
/* Decide whether we want to dump it */
selectDumpableObject(&(opfinfo[i].dobj), fout);
/* Extensions do not currently have ACLs. */
opfinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
if (fout->remoteVersion >= 70300)
{
if (strlen(opfinfo[i].rolname) == 0)
......@@ -5123,6 +5170,7 @@ getTables(Archive *fout, int *numTables)
int i_toastreloptions;
int i_reloftype;
int i_relpages;
int i_changed_acl;
/* Make sure we are in proper schema */
selectSourceSchema(fout, "pg_catalog");
......@@ -5154,6 +5202,11 @@ getTables(Archive *fout, int *numTables)
PQExpBuffer initacl_subquery = createPQExpBuffer();
PQExpBuffer initracl_subquery = createPQExpBuffer();
PQExpBuffer attacl_subquery = createPQExpBuffer();
PQExpBuffer attracl_subquery = createPQExpBuffer();
PQExpBuffer attinitacl_subquery = createPQExpBuffer();
PQExpBuffer attinitracl_subquery = createPQExpBuffer();
/*
* Left join to pick up dependency info linking sequences to their
* owning column, if any (note this dependency is AUTO as of 8.2)
......@@ -5167,6 +5220,10 @@ getTables(Archive *fout, int *numTables)
"CASE WHEN c.relkind = 'S' THEN 's' ELSE 'r' END::\"char\"",
dopt->binary_upgrade);
buildACLQueries(attacl_subquery, attracl_subquery, attinitacl_subquery,
attinitracl_subquery, "at.attacl", "c.relowner", "'c'",
dopt->binary_upgrade);
appendPQExpBuffer(query,
"SELECT c.tableoid, c.oid, c.relname, "
"%s AS relacl, %s as rrelacl, "
......@@ -5188,7 +5245,17 @@ getTables(Archive *fout, int *numTables)
"array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
"WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
"tc.reloptions AS toast_reloptions "
"tc.reloptions AS toast_reloptions, "
"EXISTS (SELECT 1 FROM pg_attribute at LEFT JOIN pg_init_privs pip ON"
"(c.oid = pip.objoid AND pip.classoid = "
"(SELECT oid FROM pg_class WHERE relname = 'pg_class') AND pip.objsubid = at.attnum)"
"WHERE at.attrelid = c.oid AND ("
"%s IS NOT NULL "
"OR %s IS NOT NULL "
"OR %s IS NOT NULL "
"OR %s IS NOT NULL"
"))"
"AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5207,6 +5274,10 @@ getTables(Archive *fout, int *numTables)
initacl_subquery->data,
initracl_subquery->data,
username_subquery,
attacl_subquery->data,
attracl_subquery->data,
attinitacl_subquery->data,
attinitracl_subquery->data,
RELKIND_SEQUENCE,
RELKIND_RELATION, RELKIND_SEQUENCE,
RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
......@@ -5216,6 +5287,11 @@ getTables(Archive *fout, int *numTables)
destroyPQExpBuffer(racl_subquery);
destroyPQExpBuffer(initacl_subquery);
destroyPQExpBuffer(initracl_subquery);
destroyPQExpBuffer(attacl_subquery);
destroyPQExpBuffer(attracl_subquery);
destroyPQExpBuffer(attinitacl_subquery);
destroyPQExpBuffer(attinitracl_subquery);
}
else if (fout->remoteVersion >= 90500)
{
......@@ -5245,7 +5321,8 @@ getTables(Archive *fout, int *numTables)
"array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
"WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
"tc.reloptions AS toast_reloptions "
"tc.reloptions AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5290,7 +5367,8 @@ getTables(Archive *fout, int *numTables)
"array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
"WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
"tc.reloptions AS toast_reloptions "
"tc.reloptions AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5335,7 +5413,8 @@ getTables(Archive *fout, int *numTables)
"array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
"CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
"WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
"tc.reloptions AS toast_reloptions "
"tc.reloptions AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5378,7 +5457,8 @@ getTables(Archive *fout, int *numTables)
"d.refobjsubid AS owning_col, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"c.reloptions AS reloptions, "
"tc.reloptions AS toast_reloptions "
"tc.reloptions AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5421,7 +5501,8 @@ getTables(Archive *fout, int *numTables)
"d.refobjsubid AS owning_col, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"c.reloptions AS reloptions, "
"tc.reloptions AS toast_reloptions "
"tc.reloptions AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5463,7 +5544,8 @@ getTables(Archive *fout, int *numTables)
"d.refobjsubid AS owning_col, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"c.reloptions AS reloptions, "
"tc.reloptions AS toast_reloptions "
"tc.reloptions AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5505,7 +5587,8 @@ getTables(Archive *fout, int *numTables)
"d.refobjsubid AS owning_col, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"c.reloptions AS reloptions, "
"NULL AS toast_reloptions "
"NULL AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5546,7 +5629,8 @@ getTables(Archive *fout, int *numTables)
"d.refobjsubid AS owning_col, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
"NULL AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5586,7 +5670,8 @@ getTables(Archive *fout, int *numTables)
"d.refobjsubid AS owning_col, "
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
"NULL AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
......@@ -5623,7 +5708,8 @@ getTables(Archive *fout, int *numTables)
"NULL::int4 AS owning_col, "
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
"NULL AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class "
"WHERE relkind IN ('%c', '%c', '%c') "
"ORDER BY oid",
......@@ -5655,7 +5741,8 @@ getTables(Archive *fout, int *numTables)
"NULL::int4 AS owning_col, "
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
"NULL AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class "
"WHERE relkind IN ('%c', '%c', '%c') "
"ORDER BY oid",
......@@ -5695,7 +5782,8 @@ getTables(Archive *fout, int *numTables)
"NULL::int4 AS owning_col, "
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
"NULL AS toast_reloptions, "
"NULL AS changed_acl "
"FROM pg_class c "
"WHERE relkind IN ('%c', '%c') "
"ORDER BY oid",
......@@ -5754,6 +5842,7 @@ getTables(Archive *fout, int *numTables)
i_checkoption = PQfnumber(res, "checkoption");
i_toastreloptions = PQfnumber(res, "toast_reloptions");
i_reloftype = PQfnumber(res, "reloftype");
i_changed_acl = PQfnumber(res, "changed_acl");
if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300)
{
......@@ -5835,6 +5924,21 @@ getTables(Archive *fout, int *numTables)
else
selectDumpableTable(&tblinfo[i], fout);
/*
* If the table-level and all column-level ACLs for this table are
* unchanged, then we don't need to worry about including the ACLs
* for this table. If any column-level ACLs have been changed, the
* 'changed_acl' column from the query will indicate that.
*
* This can result in a significant performance improvement in cases
* where we are only looking to dump out the ACL (eg: pg_catalog).
*/
if (PQgetisnull(res, i, i_relacl) && PQgetisnull(res, i, i_rrelacl) &&
PQgetisnull(res, i, i_initrelacl) &&
PQgetisnull(res, i, i_initrrelacl) &&
strcmp(PQgetvalue(res, i, i_changed_acl), "f") == 0)
tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false;
tblinfo[i].postponed_def = false; /* might get set during sort */
......@@ -6989,6 +7093,9 @@ getEventTriggers(Archive *fout, int *numEventTriggers)
/* Decide whether we want to dump it */
selectDumpableObject(&(evtinfo[i].dobj), fout);
/* Event Triggers do not currently have ACLs. */
evtinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
}
PQclear(res);
......@@ -7350,6 +7457,9 @@ getCasts(Archive *fout, int *numCasts)
/* Decide whether we want to dump it */
selectDumpableCast(&(castinfo[i]), fout);
/* Casts do not currently have ACLs. */
castinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
}
PQclear(res);
......@@ -8143,6 +8253,9 @@ getTSParsers(Archive *fout, int *numTSParsers)
/* Decide whether we want to dump it */
selectDumpableObject(&(prsinfo[i].dobj), fout);
/* Text Search Parsers do not currently have ACLs. */
prsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
}
PQclear(res);
......@@ -8228,6 +8341,9 @@ getTSDictionaries(Archive *fout, int *numTSDicts)
/* Decide whether we want to dump it */
selectDumpableObject(&(dictinfo[i].dobj), fout);
/* Text Search Dictionaries do not currently have ACLs. */
dictinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
}
PQclear(res);
......@@ -8305,6 +8421,9 @@ getTSTemplates(Archive *fout, int *numTSTemplates)
/* Decide whether we want to dump it */
selectDumpableObject(&(tmplinfo[i].dobj), fout);
/* Text Search Templates do not currently have ACLs. */
tmplinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
}
PQclear(res);
......@@ -8383,6 +8502,9 @@ getTSConfigurations(Archive *fout, int *numTSConfigs)
/* Decide whether we want to dump it */
selectDumpableObject(&(cfginfo[i].dobj), fout);
/* Text Search Configurations do not currently have ACLs. */
cfginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
}
PQclear(res);
......@@ -14873,13 +14995,21 @@ dumpTable(Archive *fout, TableInfo *tbinfo)
" at.attrelid = pip.objoid AND at.attnum = pip.objsubid) "
"WHERE at.attrelid = '%u' AND "
"NOT at.attisdropped "
"AND at.attacl IS NOT NULL "
"AND ("
"%s IS NOT NULL OR "
"%s IS NOT NULL OR "
"%s IS NOT NULL OR "
"%s IS NOT NULL)"
"ORDER BY at.attnum",
acl_subquery->data,
racl_subquery->data,
initacl_subquery->data,
initracl_subquery->data,
tbinfo->dobj.catId.oid);
tbinfo->dobj.catId.oid,
acl_subquery->data,
racl_subquery->data,
initacl_subquery->data,
initracl_subquery->data);
destroyPQExpBuffer(acl_subquery);
destroyPQExpBuffer(racl_subquery);
......
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