Skip to content

Commit ea91253

Browse files
committed
Avoid trying to restore table ACLs and per-column ACLs in parallel.
Parallel pg_restore has always supposed that ACL items for different objects are independent and can be restored in parallel without conflicts. However, there is one case where this fails: because REVOKE on a table is defined to also revoke the privilege(s) at column level, we can't restore per-column ACLs till after we restore any table-level privileges on their table. Failure to honor this restriction can lead to "tuple concurrently updated" errors during parallel restore, or even to the per-column ACLs silently disappearing because the table-level REVOKE is executed afterwards. To fix, add a dependency from each column-level ACL item to its table's ACL item, if there is one. Note that this doesn't fix the hazard for pre-existing archive files, only for ones made with a corrected pg_dump. Given that the bug's been there quite awhile without field reports, I think this is acceptable. This requires changing the API of pg_dump's dumpACL() function. To keep its argument list from getting even longer, I removed the "CatalogId objCatId" argument, which has been unused for ages. Per report from Justin Pryzby. Back-patch to all supported branches. Discussion: https://postgr.es/m/20200706050129.GW4107@telsasoft.com
1 parent 64fe120 commit ea91253

File tree

3 files changed

+64
-35
lines changed

3 files changed

+64
-35
lines changed

src/bin/pg_dump/common.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*/
2929
static DumpableObject **dumpIdMap = NULL;
3030
static int allocedDumpIds = 0;
31-
static DumpId lastDumpId = 0;
31+
static DumpId lastDumpId = 0; /* Note: 0 is InvalidDumpId */
3232

3333
/*
3434
* Variables for mapping CatalogId to DumpableObject

src/bin/pg_dump/pg_backup.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ typedef struct
233233

234234
typedef int DumpId;
235235

236+
#define InvalidDumpId 0
237+
238+
/*
239+
* Function pointer prototypes for assorted callback methods.
240+
*/
241+
236242
typedef int (*DataDumperPtr) (Archive *AH, void *userArg);
237243

238244
typedef void (*SetupWorkerPtrType) (Archive *AH);

src/bin/pg_dump/pg_dump.c

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,11 @@ static void dumpUserMappings(Archive *fout,
220220
const char *owner, CatalogId catalogId, DumpId dumpId);
221221
static void dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo);
222222

223-
static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
224-
const char *type, const char *name, const char *subname,
225-
const char *nspname, const char *owner,
226-
const char *acls, const char *racls,
227-
const char *initacls, const char *initracls);
223+
static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
224+
const char *type, const char *name, const char *subname,
225+
const char *nspname, const char *owner,
226+
const char *acls, const char *racls,
227+
const char *initacls, const char *initracls);
228228

229229
static void getDependencies(Archive *fout);
230230
static void BuildArchiveDependencies(Archive *fout);
@@ -2992,7 +2992,7 @@ dumpDatabase(Archive *fout)
29922992
* Dump ACL if any. Note that we do not support initial privileges
29932993
* (pg_init_privs) on databases.
29942994
*/
2995-
dumpACL(fout, dbCatId, dbDumpId, "DATABASE",
2995+
dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
29962996
qdatname, NULL, NULL,
29972997
dba, datacl, rdatacl, "", "");
29982998

@@ -3477,7 +3477,7 @@ dumpBlob(Archive *fout, BlobInfo *binfo)
34773477

34783478
/* Dump ACL if any */
34793479
if (binfo->blobacl && (binfo->dobj.dump & DUMP_COMPONENT_ACL))
3480-
dumpACL(fout, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT",
3480+
dumpACL(fout, binfo->dobj.dumpId, InvalidDumpId, "LARGE OBJECT",
34813481
binfo->dobj.name, NULL,
34823482
NULL, binfo->rolname, binfo->blobacl, binfo->rblobacl,
34833483
binfo->initblobacl, binfo->initrblobacl);
@@ -10155,7 +10155,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
1015510155
nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
1015610156

1015710157
if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
10158-
dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
10158+
dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
1015910159
qnspname, NULL, NULL,
1016010160
nspinfo->rolname, nspinfo->nspacl, nspinfo->rnspacl,
1016110161
nspinfo->initnspacl, nspinfo->initrnspacl);
@@ -10439,7 +10439,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
1043910439
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
1044010440

1044110441
if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10442-
dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
10442+
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
1044310443
qtypname, NULL,
1044410444
tyinfo->dobj.namespace->dobj.name,
1044510445
tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
@@ -10565,7 +10565,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
1056510565
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
1056610566

1056710567
if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10568-
dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
10568+
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
1056910569
qtypname, NULL,
1057010570
tyinfo->dobj.namespace->dobj.name,
1057110571
tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
@@ -10637,7 +10637,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
1063710637
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
1063810638

1063910639
if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10640-
dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
10640+
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
1064110641
qtypname, NULL,
1064210642
tyinfo->dobj.namespace->dobj.name,
1064310643
tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
@@ -10918,7 +10918,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
1091810918
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
1091910919

1092010920
if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10921-
dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
10921+
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
1092210922
qtypname, NULL,
1092310923
tyinfo->dobj.namespace->dobj.name,
1092410924
tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
@@ -11074,7 +11074,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
1107411074
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
1107511075

1107611076
if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11077-
dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
11077+
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
1107811078
qtypname, NULL,
1107911079
tyinfo->dobj.namespace->dobj.name,
1108011080
tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
@@ -11296,7 +11296,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
1129611296
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
1129711297

1129811298
if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11299-
dumpACL(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, "TYPE",
11299+
dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
1130011300
qtypname, NULL,
1130111301
tyinfo->dobj.namespace->dobj.name,
1130211302
tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
@@ -11596,7 +11596,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
1159611596
plang->dobj.catId, 0, plang->dobj.dumpId);
1159711597

1159811598
if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
11599-
dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
11599+
dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
1160011600
qlanname, NULL, NULL,
1160111601
plang->lanowner, plang->lanacl, plang->rlanacl,
1160211602
plang->initlanacl, plang->initrlanacl);
@@ -12306,7 +12306,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
1230612306
finfo->dobj.catId, 0, finfo->dobj.dumpId);
1230712307

1230812308
if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
12309-
dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, keyword,
12309+
dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
1231012310
funcsig, NULL,
1231112311
finfo->dobj.namespace->dobj.name,
1231212312
finfo->rolname, finfo->proacl, finfo->rproacl,
@@ -14304,7 +14304,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
1430414304
aggsig = format_function_signature(fout, &agginfo->aggfn, true);
1430514305

1430614306
if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
14307-
dumpACL(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId,
14307+
dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
1430814308
"FUNCTION", aggsig, NULL,
1430914309
agginfo->aggfn.dobj.namespace->dobj.name,
1431014310
agginfo->aggfn.rolname, agginfo->aggfn.proacl,
@@ -14706,7 +14706,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo)
1470614706

1470714707
/* Handle the ACL */
1470814708
if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
14709-
dumpACL(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
14709+
dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
1471014710
"FOREIGN DATA WRAPPER", qfdwname, NULL,
1471114711
NULL, fdwinfo->rolname,
1471214712
fdwinfo->fdwacl, fdwinfo->rfdwacl,
@@ -14795,7 +14795,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo)
1479514795

1479614796
/* Handle the ACL */
1479714797
if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
14798-
dumpACL(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
14798+
dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
1479914799
"FOREIGN SERVER", qsrvname, NULL,
1480014800
NULL, srvinfo->rolname,
1480114801
srvinfo->srvacl, srvinfo->rsrvacl,
@@ -14988,8 +14988,9 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
1498814988
/*----------
1498914989
* Write out grant/revoke information
1499014990
*
14991-
* 'objCatId' is the catalog ID of the underlying object.
1499214991
* 'objDumpId' is the dump ID of the underlying object.
14992+
* 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
14993+
* or InvalidDumpId if there is no need for a second dependency.
1499314994
* 'type' must be one of
1499414995
* TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
1499514996
* FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
@@ -15012,25 +15013,29 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
1501215013
* NB: initacls/initracls are needed because extensions can set privileges on
1501315014
* an object during the extension's script file and we record those into
1501415015
* pg_init_privs as that object's initial privileges.
15016+
*
15017+
* Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
15018+
* no ACL entry was created.
1501515019
*----------
1501615020
*/
15017-
static void
15018-
dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
15021+
static DumpId
15022+
dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
1501915023
const char *type, const char *name, const char *subname,
1502015024
const char *nspname, const char *owner,
1502115025
const char *acls, const char *racls,
1502215026
const char *initacls, const char *initracls)
1502315027
{
15028+
DumpId aclDumpId = InvalidDumpId;
1502415029
DumpOptions *dopt = fout->dopt;
1502515030
PQExpBuffer sql;
1502615031

1502715032
/* Do nothing if ACL dump is not enabled */
1502815033
if (dopt->aclsSkip)
15029-
return;
15034+
return InvalidDumpId;
1503015035

1503115036
/* --data-only skips ACLs *except* BLOB ACLs */
1503215037
if (dopt->dataOnly && strcmp(type, "LARGE OBJECT") != 0)
15033-
return;
15038+
return InvalidDumpId;
1503415039

1503515040
sql = createPQExpBuffer();
1503615041

@@ -15062,25 +15067,36 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
1506215067
if (sql->len > 0)
1506315068
{
1506415069
PQExpBuffer tag = createPQExpBuffer();
15070+
DumpId aclDeps[2];
15071+
int nDeps = 0;
1506515072

1506615073
if (subname)
1506715074
appendPQExpBuffer(tag, "COLUMN %s.%s", name, subname);
1506815075
else
1506915076
appendPQExpBuffer(tag, "%s %s", type, name);
1507015077

15071-
ArchiveEntry(fout, nilCatalogId, createDumpId(),
15078+
aclDeps[nDeps++] = objDumpId;
15079+
if (altDumpId != InvalidDumpId)
15080+
aclDeps[nDeps++] = altDumpId;
15081+
15082+
aclDumpId = createDumpId();
15083+
15084+
ArchiveEntry(fout, nilCatalogId, aclDumpId,
1507215085
ARCHIVE_OPTS(.tag = tag->data,
1507315086
.namespace = nspname,
1507415087
.owner = owner,
1507515088
.description = "ACL",
1507615089
.section = SECTION_NONE,
1507715090
.createStmt = sql->data,
15078-
.deps = &objDumpId,
15079-
.nDeps = 1));
15091+
.deps = aclDeps,
15092+
.nDeps = nDeps));
15093+
1508015094
destroyPQExpBuffer(tag);
1508115095
}
1508215096

1508315097
destroyPQExpBuffer(sql);
15098+
15099+
return aclDumpId;
1508415100
}
1508515101

1508615102
/*
@@ -15406,6 +15422,7 @@ static void
1540615422
dumpTable(Archive *fout, TableInfo *tbinfo)
1540715423
{
1540815424
DumpOptions *dopt = fout->dopt;
15425+
DumpId tableAclDumpId = InvalidDumpId;
1540915426
char *namecopy;
1541015427

1541115428
/*
@@ -15427,11 +15444,12 @@ dumpTable(Archive *fout, TableInfo *tbinfo)
1542715444
const char *objtype =
1542815445
(tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
1542915446

15430-
dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
15431-
objtype, namecopy, NULL,
15432-
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
15433-
tbinfo->relacl, tbinfo->rrelacl,
15434-
tbinfo->initrelacl, tbinfo->initrrelacl);
15447+
tableAclDumpId =
15448+
dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
15449+
objtype, namecopy, NULL,
15450+
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
15451+
tbinfo->relacl, tbinfo->rrelacl,
15452+
tbinfo->initrelacl, tbinfo->initrrelacl);
1543515453
}
1543615454

1543715455
/*
@@ -15515,8 +15533,13 @@ dumpTable(Archive *fout, TableInfo *tbinfo)
1551515533
char *attnamecopy;
1551615534

1551715535
attnamecopy = pg_strdup(fmtId(attname));
15518-
/* Column's GRANT type is always TABLE */
15519-
dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
15536+
15537+
/*
15538+
* Column's GRANT type is always TABLE. Each column ACL depends
15539+
* on the table-level ACL, since we can restore column ACLs in
15540+
* parallel but the table-level ACL has to be done first.
15541+
*/
15542+
dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
1552015543
"TABLE", namecopy, attnamecopy,
1552115544
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
1552215545
attacl, rattacl, initattacl, initrattacl);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy