From dc0e76ca36b4f7b91c3b83e53012585c5eccbe98 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Tue, 13 Jul 2004 03:00:17 +0000 Subject: [PATCH] Change pg_dump to use ALTER OWNER commands instead of SET SESSION AUTHORIZATION commands by default. Move all GRANT and REVOKE commands to the end of the dump to avoid restore failures in several situations. Bring back --use-set-session-authorization option to get previous SET behaviour Christopher Kings-Lyne --- doc/src/sgml/ref/pg_dump.sgml | 10 +- doc/src/sgml/ref/pg_restore.sgml | 10 +- src/bin/pg_dump/pg_backup.h | 3 +- src/bin/pg_dump/pg_backup_archiver.c | 177 ++++++++++++++++++++++----- src/bin/pg_dump/pg_dump.c | 8 +- src/bin/pg_dump/pg_restore.c | 8 +- 6 files changed, 168 insertions(+), 48 deletions(-) diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index b3dd617136..72ec981fcc 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -1,5 +1,5 @@ @@ -464,10 +464,10 @@ PostgreSQL documentation - This option is obsolete but still accepted for backwards - compatibility. - pg_dump now always behaves in the - way formerly selected by this option. + Output SQL standard SET SESSION AUTHORIZATION commands instead + of OWNER TO commands. This makes the dump more standards compatible, + but depending on the history of the objects in the dump, may not + restore properly. diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml index 8f4c1cd9b9..533bdd9c0b 100644 --- a/doc/src/sgml/ref/pg_restore.sgml +++ b/doc/src/sgml/ref/pg_restore.sgml @@ -1,4 +1,4 @@ - + @@ -337,10 +337,10 @@ - This option is obsolete but still accepted for backwards - compatibility. - pg_restore now always behaves in the - way formerly selected by this option. + Output SQL standard SET SESSION AUTHORIZATION commands instead + of OWNER TO commands. This makes the dump more standards compatible, + but depending on the history of the objects in the dump, may not + restore properly. diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 317646252c..4957ea9a14 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup.h,v 1.30 2004/04/22 02:39:09 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup.h,v 1.31 2004/07/13 03:00:17 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -73,6 +73,7 @@ typedef struct _restoreOptions int noOwner; /* Don't try to match original object owner */ int disable_triggers; /* disable triggers during * data-only restore */ + int use_setsessauth; /* Use SET SESSION AUTHORIZATION commands instead of OWNER TO */ char *superuser; /* Username to use as superuser */ int dataOnly; int dropSchema; diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index b3d89a2091..19b0804888 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.87 2004/05/19 21:21:26 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.88 2004/07/13 03:00:17 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -47,7 +47,10 @@ static char *modulename = gettext_noop("archiver"); static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt, const int compression, ArchiveMode mode); -static int _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData); +static char *_getObjectFromDropStmt(const char *dropStmt, const char *type); +static void _printTocHeader(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData); +static int _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData, bool ownerAndACL); + static void fixPriorBlobRefs(ArchiveHandle *AH, TocEntry *blobte, RestoreOptions *ropt); @@ -59,7 +62,7 @@ static void _becomeUser(ArchiveHandle *AH, const char *user); static void _becomeOwner(ArchiveHandle *AH, TocEntry *te); static void _selectOutputSchema(ArchiveHandle *AH, const char *schemaName); -static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt); +static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool ownerAndACL); static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt); static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt); static TocEntry *getTocEntryByDumpId(ArchiveHandle *AH, DumpId id); @@ -181,7 +184,7 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) impliedDataOnly = 1; while (te != AH->toc) { - reqs = _tocEntryRequired(te, ropt); + reqs = _tocEntryRequired(te, ropt, false); if ((reqs & REQ_SCHEMA) != 0) { /* It's schema, and it's wanted */ impliedDataOnly = 0; @@ -217,7 +220,7 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) te = AH->toc->prev; while (te != AH->toc) { - reqs = _tocEntryRequired(te, ropt); + reqs = _tocEntryRequired(te, ropt, false); if (((reqs & REQ_SCHEMA) != 0) && te->dropStmt) { /* We want the schema */ @@ -239,7 +242,7 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) while (te != AH->toc) { /* Work out what, if anything, we want from this entry */ - reqs = _tocEntryRequired(te, ropt); + reqs = _tocEntryRequired(te, ropt, false); /* Dump any relevant dump warnings to stderr */ if (!ropt->suppressDumpWarnings && strcmp(te->desc, "WARNING") == 0) @@ -256,7 +259,7 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) { ahlog(AH, 1, "creating %s %s\n", te->desc, te->tag); - _printTocEntry(AH, te, ropt, false); + _printTocEntry(AH, te, ropt, false, false); defnDumped = true; /* If we created a DB, connect to it... */ @@ -290,7 +293,7 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) die_horribly(AH, modulename, "cannot restore from compressed archive (not configured for compression support)\n"); #endif - _printTocEntry(AH, te, ropt, true); + _printTocEntry(AH, te, ropt, true, false); /* * Maybe we can't do BLOBS, so check if this node is @@ -361,12 +364,34 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt) { /* If we haven't already dumped the defn part, do so now */ ahlog(AH, 1, "executing %s %s\n", te->desc, te->tag); - _printTocEntry(AH, te, ropt, false); + _printTocEntry(AH, te, ropt, false, false); } } te = te->next; } /* end loop over TOC entries */ + /* + * Scan TOC again to output ownership commands and ACLs + */ + te = AH->toc->next; + while (te != AH->toc) + { + /* Work out what, if anything, we want from this entry */ + reqs = _tocEntryRequired(te, ropt, true); + + defnDumped = false; + + if ((reqs & REQ_SCHEMA) != 0) /* We want the schema */ + { + ahlog(AH, 1, "setting owner and acl for %s %s\n", te->desc, te->tag); + + _printTocEntry(AH, te, ropt, false, true); + defnDumped = true; + } + + te = te->next; + } + /* * Clean up & we're done. */ @@ -408,7 +433,7 @@ fixPriorBlobRefs(ArchiveHandle *AH, TocEntry *blobte, RestoreOptions *ropt) { if (strcmp(te->desc, "TABLE DATA") == 0) { - reqs = _tocEntryRequired(te, ropt); + reqs = _tocEntryRequired(te, ropt, false); if ((reqs & REQ_DATA) != 0) /* We loaded the data */ { @@ -659,7 +684,7 @@ PrintTOCSummary(Archive *AHX, RestoreOptions *ropt) while (te != AH->toc) { - if (_tocEntryRequired(te, ropt) != 0) + if (_tocEntryRequired(te, ropt, false) != 0) ahprintf(AH, "%d; %u %u %s %s %s\n", te->dumpId, te->catalogId.tableoid, te->catalogId.oid, te->desc, te->tag, te->owner); @@ -1270,7 +1295,7 @@ TocIDRequired(ArchiveHandle *AH, DumpId id, RestoreOptions *ropt) if (!te) return 0; - return _tocEntryRequired(te, ropt); + return _tocEntryRequired(te, ropt, false); } size_t @@ -1888,7 +1913,7 @@ ReadToc(ArchiveHandle *AH) } static teReqs -_tocEntryRequired(TocEntry *te, RestoreOptions *ropt) +_tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool ownerAndACL) { teReqs res = 3; /* Schema = 1, Data = 2, Both = 3 */ @@ -1897,7 +1922,7 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt) return 0; /* If it's an ACL, maybe ignore it */ - if (ropt->aclsSkip && strcmp(te->desc, "ACL") == 0) + if ((!ownerAndACL || ropt->aclsSkip) && strcmp(te->desc, "ACL") == 0) return 0; if (!ropt->create && strcmp(te->desc, "DATABASE") == 0) @@ -2159,7 +2184,7 @@ _becomeUser(ArchiveHandle *AH, const char *user) static void _becomeOwner(ArchiveHandle *AH, TocEntry *te) { - if (AH->ropt && AH->ropt->noOwner) + if (AH->ropt && (AH->ropt->noOwner || !AH->ropt->use_setsessauth)) return; _becomeUser(AH, te->owner); @@ -2224,18 +2249,65 @@ _selectOutputSchema(ArchiveHandle *AH, const char *schemaName) } +/** + * Parses the dropStmt part of a TOC entry and returns + * a newly allocated string that is the object identifier + * The caller must free the result. + */ +static char * +_getObjectFromDropStmt(const char *dropStmt, const char *type) +{ + /* Chop "DROP" off the front and make a copy */ + char *first = strdup(dropStmt + 5); + char *last = first + strlen(first) - 1; /* Points to the last real char in extract */ + char *buf = NULL; -static int -_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData) + /* Loop from the end of the string until last char is no longer '\n' or ';' */ + while (last >= first && (*last == '\n' || *last == ';')) { + last--; + } + + /* Insert end of string one place after last */ + *(last + 1) = '\0'; + + /* Take off CASCADE if necessary. Only TYPEs seem to have this, but may + * as well check for all */ + if ((last - first) >= 8) { + if (strcmp(last - 7, " CASCADE") == 0) + last -= 8; + } + + /* Insert end of string one place after last */ + *(last + 1) = '\0'; + + /* Special case VIEWs and SEQUENCEs. They must use ALTER TABLE. */ + if (strcmp(type, "VIEW") == 0 && (last - first) >= 5) + { + int len = 6 + strlen(first + 5) + 1; + buf = malloc(len); + snprintf(buf, len, "TABLE %s", first + 5); + free (first); + } + else if (strcmp(type, "SEQUENCE") == 0 && (last - first) >= 9) + { + int len = 6 + strlen(first + 9) + 1; + buf = malloc(len); + snprintf(buf, len, "TABLE %s", first + 9); + free (first); + } + else + { + buf = first; + } + + return buf; +} + +static void +_printTocHeader(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData) { const char *pfx; - /* Select owner and schema as necessary */ - _becomeOwner(AH, te); - _selectOutputSchema(AH, te->namespace); - if (strcmp(te->desc, "TABLE") == 0) - _setWithOids(AH, te); - if (isData) pfx = "Data for "; else @@ -2263,21 +2335,60 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat if (AH->PrintExtraTocPtr != NULL) (*AH->PrintExtraTocPtr) (AH, te); ahprintf(AH, "--\n\n"); +} - /* - * Really crude hack for suppressing AUTHORIZATION clause of CREATE SCHEMA - * when --no-owner mode is selected. This is ugly, but I see no other - * good way ... - */ - if (AH->ropt && AH->ropt->noOwner && strcmp(te->desc, "SCHEMA") == 0) +static int +_printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData, bool ownerAndACL) +{ + /* Select schema as necessary */ + _becomeOwner(AH, te); + _selectOutputSchema(AH, te->namespace); + if (strcmp(te->desc, "TABLE") == 0 && !ownerAndACL) + _setWithOids(AH, te); + + if (!ropt->noOwner && !ropt->use_setsessauth && ownerAndACL && strlen(te->owner) > 0 && strlen(te->dropStmt) > 0 && ( + strcmp(te->desc, "AGGREGATE") == 0 || + strcmp(te->desc, "CONVERSION") == 0 || + strcmp(te->desc, "DOMAIN") == 0 || + strcmp(te->desc, "FUNCTION") == 0 || + strcmp(te->desc, "OPERATOR") == 0 || + strcmp(te->desc, "OPERATOR CLASS") == 0 || + strcmp(te->desc, "TABLE") == 0 || + strcmp(te->desc, "TYPE") == 0 || + strcmp(te->desc, "VIEW") == 0 || + strcmp(te->desc, "SEQUENCE") == 0 + )) { - ahprintf(AH, "CREATE SCHEMA %s;\n\n\n", te->tag); + char *temp = _getObjectFromDropStmt(te->dropStmt, te->desc); + _printTocHeader(AH, te, ropt, isData); + ahprintf(AH, "ALTER %s OWNER TO %s;\n\n", temp, fmtId(te->owner)); + free (temp); + } + else if (ownerAndACL && strcmp(te->desc, "ACL") == 0) + { + _printTocHeader(AH, te, ropt, isData); + ahprintf(AH, "%s\n\n", te->defn); } - else + else if (!ownerAndACL && strlen(te->defn) > 0) { - /* normal case */ - if (strlen(te->defn) > 0) + _printTocHeader(AH, te, ropt, isData); + + /* + * Really crude hack for suppressing AUTHORIZATION clause of CREATE SCHEMA + * when --no-owner mode is selected. This is ugly, but I see no other + * good way ... + */ + if (AH->ropt && AH->ropt->noOwner && strcmp(te->desc, "SCHEMA") == 0) + { + ahprintf(AH, "CREATE SCHEMA %s;\n\n\n", te->tag); + } + else + { ahprintf(AH, "%s\n\n", te->defn); + } + } + else if (isData) { + _printTocHeader(AH, te, ropt, isData); } return 1; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index c01ea6ad5b..9e38b0d43a 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.378 2004/07/12 05:37:53 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.379 2004/07/13 03:00:17 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -392,7 +392,7 @@ main(int argc, char **argv) else if (strcmp(optarg, "disable-triggers") == 0) disable_triggers = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) - /* no-op, still allowed for compatibility */ ; + use_setsessauth = 1; else { fprintf(stderr, @@ -636,6 +636,7 @@ main(int argc, char **argv) ropt->create = outputCreate; ropt->noOwner = outputNoOwner; ropt->disable_triggers = disable_triggers; + ropt->use_setsessauth = use_setsessauth; if (compressLevel == -1) ropt->compression = 0; @@ -693,6 +694,9 @@ help(const char *progname) " disable dollar quoting, use SQL standard quoting\n")); printf(_(" -X disable-triggers, --disable-triggers\n" " disable triggers during data-only restore\n")); + printf(_(" -X use-set-session-authorization, --use-set-session-authorization\n" + " use SESSION AUTHORIZATION commands instead of\n" + " OWNER TO commands\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c index 9b0dd312da..eef86a25e5 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -34,7 +34,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_restore.c,v 1.58 2004/06/03 00:07:37 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_restore.c,v 1.59 2004/07/13 03:00:17 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -243,7 +243,7 @@ main(int argc, char **argv) case 'X': if (strcmp(optarg, "use-set-session-authorization") == 0) - /* no-op, still allowed for compatibility */ ; + use_setsessauth = 1; else if (strcmp(optarg, "disable-triggers") == 0) disable_triggers = 1; else @@ -286,6 +286,7 @@ main(int argc, char **argv) } opts->disable_triggers = disable_triggers; + opts->use_setsessauth = use_setsessauth; if (opts->formatName) { @@ -381,6 +382,9 @@ usage(const char *progname) printf(_(" -x, --no-privileges skip restoration of access privileges (grant/revoke)\n")); printf(_(" -X disable-triggers, --disable-triggers\n" " disable triggers during data-only restore\n")); + printf(_(" -X use-set-session-authorization, --use-set-session-authorization\n" + " use SESSION AUTHORIZATION commands instead of\n" + " OWNER TO commands\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));