1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-23 14:01:44 +03:00

Rewrite --section option to decouple it from --schema-only/--data-only.

The initial implementation of pg_dump's --section option supposed that the
existing --schema-only and --data-only options could be made equivalent to
--section settings.  This is wrong, though, due to dubious but long since
set-in-stone decisions about where to dump SEQUENCE SET items, as seen in
bug report from Martin Pitt.  (And I'm not totally convinced there weren't
other bugs, either.)  Undo that coupling and instead drive --section
filtering off current-section state tracked as we scan through the TOC
list to call _tocEntryRequired().

To make sure those decisions don't shift around and hopefully save a few
cycles, run _tocEntryRequired() only once per TOC entry and save the result
in a new TOC field.  This required minor rejiggering of ACL handling but
also allows a far cleaner implementation of inhibit_data_for_failed_table.

Also, to ensure that pg_dump and pg_restore have the same behavior with
respect to the --section switches, add _tocEntryRequired() filtering to
WriteToc() and WriteDataChunks(), rather than trying to implement section
filtering in an entirely orthogonal way in dumpDumpableObject().  This
required adjusting the handling of the special ENCODING and STDSTRINGS
items, but they were pretty weird before anyway.

Minor other code review for the patch, too.
This commit is contained in:
Tom Lane
2012-05-29 23:22:14 -04:00
parent 4bc6fb57f7
commit 4317e0246c
10 changed files with 238 additions and 238 deletions

View File

@ -131,7 +131,7 @@ static void _selectOutputSchema(ArchiveHandle *AH, const char *schemaName);
static void _selectTablespace(ArchiveHandle *AH, const char *tablespace);
static void processEncodingEntry(ArchiveHandle *AH, TocEntry *te);
static void processStdStringsEntry(ArchiveHandle *AH, TocEntry *te);
static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls);
static teReqs _tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt);
static bool _tocEntryIsACL(TocEntry *te);
static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
@ -234,15 +234,35 @@ CloseArchive(Archive *AHX)
/* Public */
void
RestoreArchive(Archive *AHX, RestoreOptions *ropt)
SetArchiveRestoreOptions(Archive *AHX, RestoreOptions *ropt)
{
ArchiveHandle *AH = (ArchiveHandle *) AHX;
TocEntry *te;
teSection curSection;
/* Save options for later access */
AH->ropt = ropt;
/* Decide which TOC entries will be dumped/restored, and mark them */
curSection = SECTION_PRE_DATA;
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
if (te->section != SECTION_NONE)
curSection = te->section;
te->reqs = _tocEntryRequired(te, curSection, ropt);
}
}
/* Public */
void
RestoreArchive(Archive *AHX)
{
ArchiveHandle *AH = (ArchiveHandle *) AHX;
RestoreOptions *ropt = AH->ropt;
bool parallel_mode;
TocEntry *te;
teReqs reqs;
OutputContext sav;
AH->ropt = ropt;
AH->stage = STAGE_INITIALIZING;
/*
@ -292,8 +312,7 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt)
{
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
reqs = _tocEntryRequired(te, ropt, false);
if (te->hadDumper && (reqs & REQ_DATA) != 0)
if (te->hadDumper && (te->reqs & REQ_DATA) != 0)
exit_horribly(modulename, "cannot restore from compressed archive (compression not supported in this installation)\n");
}
}
@ -345,8 +364,7 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt)
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
reqs = _tocEntryRequired(te, ropt, true);
if ((reqs & REQ_SCHEMA) != 0)
if ((te->reqs & REQ_SCHEMA) != 0)
{ /* It's schema, and it's wanted */
impliedDataOnly = 0;
break;
@ -403,9 +421,8 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt)
{
AH->currentTE = te;
reqs = _tocEntryRequired(te, ropt, false /* needn't drop ACLs */ );
/* We want anything that's selected and has a dropStmt */
if (((reqs & (REQ_SCHEMA | REQ_DATA)) != 0) && te->dropStmt)
if (((te->reqs & (REQ_SCHEMA | REQ_DATA)) != 0) && te->dropStmt)
{
ahlog(AH, 1, "dropping %s %s\n", te->desc, te->tag);
/* Select owner and schema as necessary */
@ -453,11 +470,8 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt)
{
AH->currentTE = te;
/* Work out what, if anything, we want from this entry */
reqs = _tocEntryRequired(te, ropt, true);
/* Both schema and data objects might now have ownership/ACLs */
if ((reqs & (REQ_SCHEMA | REQ_DATA)) != 0)
if ((te->reqs & (REQ_SCHEMA | REQ_DATA)) != 0)
{
ahlog(AH, 1, "setting owner and privileges for %s %s\n",
te->desc, te->tag);
@ -508,7 +522,18 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te,
AH->currentTE = te;
/* Work out what, if anything, we want from this entry */
reqs = _tocEntryRequired(te, ropt, false);
if (_tocEntryIsACL(te))
reqs = 0; /* ACLs are never restored here */
else
reqs = te->reqs;
/*
* Ignore DATABASE entry unless we should create it. We must check this
* here, not in _tocEntryRequired, because the createDB option should
* not affect emitting a DATABASE entry to an archive file.
*/
if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0)
reqs = 0;
/* Dump any relevant dump warnings to stderr */
if (!ropt->suppressDumpWarnings && strcmp(te->desc, "WARNING") == 0)
@ -588,7 +613,7 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te,
/*
* If we can output the data, then restore it.
*/
if (AH->PrintTocDataPtr !=NULL && (reqs & REQ_DATA) != 0)
if (AH->PrintTocDataPtr !=NULL)
{
_printTocEntry(AH, te, ropt, true, false);
@ -841,8 +866,9 @@ PrintTOCSummary(Archive *AHX, RestoreOptions *ropt)
{
ArchiveHandle *AH = (ArchiveHandle *) AHX;
TocEntry *te;
teSection curSection;
OutputContext sav;
char *fmtName;
const char *fmtName;
sav = SaveOutput(AH);
if (ropt->filename)
@ -880,9 +906,13 @@ PrintTOCSummary(Archive *AHX, RestoreOptions *ropt)
/* We should print DATABASE entries whether or not -C was specified */
ropt->createDB = 1;
curSection = SECTION_PRE_DATA;
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
if (ropt->verbose || _tocEntryRequired(te, ropt, true) != 0)
if (te->section != SECTION_NONE)
curSection = te->section;
if (ropt->verbose ||
(_tocEntryRequired(te, curSection, ropt) & (REQ_SCHEMA | REQ_DATA)) != 0)
ahprintf(AH, "%d; %u %u %s %s %s %s\n", te->dumpId,
te->catalogId.tableoid, te->catalogId.oid,
te->desc, te->namespace ? te->namespace : "-",
@ -1135,19 +1165,6 @@ SortTocFromFile(Archive *AHX, RestoreOptions *ropt)
strerror(errno));
}
/*
* Set up a dummy ID filter that selects all dump IDs
*/
void
InitDummyWantedList(Archive *AHX, RestoreOptions *ropt)
{
ArchiveHandle *AH = (ArchiveHandle *) AHX;
/* Allocate space for the 'wanted' array, and init it to 1's */
ropt->idWanted = (bool *) pg_malloc(sizeof(bool) * AH->maxDumpId);
memset(ropt->idWanted, 1, sizeof(bool) * AH->maxDumpId);
}
/**********************
* 'Convenience functions that look like standard IO functions
* for writing data when in dump mode.
@ -1591,14 +1608,14 @@ getTocEntryByDumpId(ArchiveHandle *AH, DumpId id)
}
teReqs
TocIDRequired(ArchiveHandle *AH, DumpId id, RestoreOptions *ropt)
TocIDRequired(ArchiveHandle *AH, DumpId id)
{
TocEntry *te = getTocEntryByDumpId(AH, id);
if (!te)
return 0;
return _tocEntryRequired(te, ropt, true);
return te->reqs;
}
size_t
@ -2082,7 +2099,7 @@ WriteDataChunks(ArchiveHandle *AH)
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
if (te->dataDumper != NULL)
if (te->dataDumper != NULL && (te->reqs & REQ_DATA) != 0)
{
AH->currToc = te;
/* printf("Writing data for %d (%x)\n", te->id, te); */
@ -2123,14 +2140,26 @@ WriteToc(ArchiveHandle *AH)
{
TocEntry *te;
char workbuf[32];
int tocCount;
int i;
/* printf("%d TOC Entries to save\n", AH->tocCount); */
/* count entries that will actually be dumped */
tocCount = 0;
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_SPECIAL)) != 0)
tocCount++;
}
WriteInt(AH, AH->tocCount);
/* printf("%d TOC Entries to save\n", tocCount); */
WriteInt(AH, tocCount);
for (te = AH->toc->next; te != AH->toc; te = te->next)
{
if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_SPECIAL)) == 0)
continue;
WriteInt(AH, te->dumpId);
WriteInt(AH, te->dataDumper ? 1 : 0);
@ -2173,7 +2202,6 @@ ReadToc(ArchiveHandle *AH)
int depIdx;
int depSize;
TocEntry *te;
bool in_post_data = false;
AH->tocCount = ReadInt(AH);
AH->maxDumpId = 0;
@ -2239,12 +2267,6 @@ ReadToc(ArchiveHandle *AH)
te->section = SECTION_PRE_DATA;
}
/* will stay true even for SECTION_NONE items */
if (te->section == SECTION_POST_DATA)
in_post_data = true;
te->inPostData = in_post_data;
te->defn = ReadStr(AH);
te->dropStmt = ReadStr(AH);
@ -2373,38 +2395,43 @@ processStdStringsEntry(ArchiveHandle *AH, TocEntry *te)
}
static teReqs
_tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
_tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt)
{
teReqs res = REQ_ALL;
teReqs res = REQ_SCHEMA | REQ_DATA;
/* ENCODING and STDSTRINGS items are dumped specially, so always reject */
/* ENCODING and STDSTRINGS items are treated specially */
if (strcmp(te->desc, "ENCODING") == 0 ||
strcmp(te->desc, "STDSTRINGS") == 0)
return 0;
return REQ_SPECIAL;
/* If it's an ACL, maybe ignore it */
if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te))
if (ropt->aclsSkip && _tocEntryIsACL(te))
return 0;
/* If it's security labels, maybe ignore it */
if (ropt->no_security_labels && strcmp(te->desc, "SECURITY LABEL") == 0)
return 0;
/* Ignore DATABASE entry unless we should create it */
if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0)
return 0;
/* skip (all but) post data section as required */
/* table data is filtered if necessary lower down */
if (ropt->dumpSections != DUMP_UNSECTIONED)
/* Ignore it if section is not to be dumped/restored */
switch (curSection)
{
if (!(ropt->dumpSections & DUMP_POST_DATA) && te->inPostData)
return 0;
if (!(ropt->dumpSections & DUMP_PRE_DATA) && ! te->inPostData && strcmp(te->desc, "TABLE DATA") != 0)
case SECTION_PRE_DATA:
if (!(ropt->dumpSections & DUMP_PRE_DATA))
return 0;
break;
case SECTION_DATA:
if (!(ropt->dumpSections & DUMP_DATA))
return 0;
break;
case SECTION_POST_DATA:
if (!(ropt->dumpSections & DUMP_POST_DATA))
return 0;
break;
default:
/* shouldn't get here, really, but ignore it */
return 0;
}
/* Check options for selective dump/restore */
if (ropt->schemaNames)
{
@ -2486,7 +2513,7 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
if (ropt->schemaOnly)
res = res & REQ_SCHEMA;
/* Mask it we only want data */
/* Mask it if we only want data */
if (ropt->dataOnly)
res = res & REQ_DATA;
@ -3553,11 +3580,9 @@ restore_toc_entries_parallel(ArchiveHandle *AH)
{
if (next_work_item != NULL)
{
teReqs reqs;
/* If not to be dumped, don't waste time launching a worker */
reqs = _tocEntryRequired(next_work_item, AH->ropt, false);
if ((reqs & (REQ_SCHEMA | REQ_DATA)) == 0)
/* If not to be restored, don't waste time launching a worker */
if ((next_work_item->reqs & (REQ_SCHEMA | REQ_DATA)) == 0 ||
_tocEntryIsACL(next_work_item))
{
ahlog(AH, 1, "skipping item %d %s %s\n",
next_work_item->dumpId,
@ -4287,15 +4312,14 @@ mark_create_done(ArchiveHandle *AH, TocEntry *te)
static void
inhibit_data_for_failed_table(ArchiveHandle *AH, TocEntry *te)
{
RestoreOptions *ropt = AH->ropt;
ahlog(AH, 1, "table \"%s\" could not be created, will not restore its data\n",
te->tag);
if (AH->tableDataId[te->dumpId] != 0)
{
/* mark it unwanted; we assume idWanted array already exists */
ropt->idWanted[AH->tableDataId[te->dumpId] - 1] = false;
TocEntry *ted = AH->tocsByDumpId[AH->tableDataId[te->dumpId]];
ted->reqs = 0;
}
}