Skip to content

Commit c0d5be5

Browse files
committed
Fix up pg_dump's treatment of large object ownership and ACLs. We now emit
a separate archive entry for each BLOB, and use pg_dump's standard methods for dealing with its ownership, ACL if any, and comment if any. This means that switches like --no-owner and --no-privileges do what they're supposed to. Preliminary testing says that performance is still reasonable even with many blobs, though we'll have to see how that shakes out in the field. KaiGai Kohei, revised by me
1 parent 2b44d74 commit c0d5be5

8 files changed

+284
-248
lines changed

src/bin/pg_dump/dumputils.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.53 2010/01/02 16:57:59 momjian Exp $
11+
* $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.54 2010/02/18 01:29:10 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -486,7 +486,8 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems)
486486
* name: the object name, in the form to use in the commands (already quoted)
487487
* subname: the sub-object name, if any (already quoted); NULL if none
488488
* type: the object type (as seen in GRANT command: must be one of
489-
* TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, or TABLESPACE)
489+
* TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
490+
* FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
490491
* acls: the ACL string fetched from the database
491492
* owner: username of object owner (will be passed through fmtId); can be
492493
* NULL or empty string to indicate "no owner known"

src/bin/pg_dump/pg_backup_archiver.c

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*
1717
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.178 2010/01/19 18:39:19 tgl Exp $
18+
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.179 2010/02/18 01:29:10 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -98,6 +98,7 @@ static void _selectTablespace(ArchiveHandle *AH, const char *tablespace);
9898
static void processEncodingEntry(ArchiveHandle *AH, TocEntry *te);
9999
static void processStdStringsEntry(ArchiveHandle *AH, TocEntry *te);
100100
static teReqs _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls);
101+
static bool _tocEntryIsACL(TocEntry *te);
101102
static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
102103
static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
103104
static TocEntry *getTocEntryByDumpId(ArchiveHandle *AH, DumpId id);
@@ -329,9 +330,9 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt)
329330
AH->currentTE = te;
330331

331332
reqs = _tocEntryRequired(te, ropt, false /* needn't drop ACLs */ );
332-
if (((reqs & REQ_SCHEMA) != 0) && te->dropStmt)
333+
/* We want anything that's selected and has a dropStmt */
334+
if (((reqs & (REQ_SCHEMA|REQ_DATA)) != 0) && te->dropStmt)
333335
{
334-
/* We want the schema */
335336
ahlog(AH, 1, "dropping %s %s\n", te->desc, te->tag);
336337
/* Select owner and schema as necessary */
337338
_becomeOwner(AH, te);
@@ -381,7 +382,8 @@ RestoreArchive(Archive *AHX, RestoreOptions *ropt)
381382
/* Work out what, if anything, we want from this entry */
382383
reqs = _tocEntryRequired(te, ropt, true);
383384

384-
if ((reqs & REQ_SCHEMA) != 0) /* We want the schema */
385+
/* Both schema and data objects might now have ownership/ACLs */
386+
if ((reqs & (REQ_SCHEMA|REQ_DATA)) != 0)
385387
{
386388
ahlog(AH, 1, "setting owner and privileges for %s %s\n",
387389
te->desc, te->tag);
@@ -905,6 +907,7 @@ EndRestoreBlobs(ArchiveHandle *AH)
905907
void
906908
StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool drop)
907909
{
910+
bool old_blob_style = (AH->version < K_VERS_1_12);
908911
Oid loOid;
909912

910913
AH->blobCount++;
@@ -914,24 +917,32 @@ StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool drop)
914917

915918
ahlog(AH, 2, "restoring large object with OID %u\n", oid);
916919

917-
if (drop)
920+
/* With an old archive we must do drop and create logic here */
921+
if (old_blob_style && drop)
918922
DropBlobIfExists(AH, oid);
919923

920924
if (AH->connection)
921925
{
922-
loOid = lo_create(AH->connection, oid);
923-
if (loOid == 0 || loOid != oid)
924-
die_horribly(AH, modulename, "could not create large object %u\n",
925-
oid);
926-
926+
if (old_blob_style)
927+
{
928+
loOid = lo_create(AH->connection, oid);
929+
if (loOid == 0 || loOid != oid)
930+
die_horribly(AH, modulename, "could not create large object %u\n",
931+
oid);
932+
}
927933
AH->loFd = lo_open(AH->connection, oid, INV_WRITE);
928934
if (AH->loFd == -1)
929-
die_horribly(AH, modulename, "could not open large object\n");
935+
die_horribly(AH, modulename, "could not open large object %u\n",
936+
oid);
930937
}
931938
else
932939
{
933-
ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
934-
oid, INV_WRITE);
940+
if (old_blob_style)
941+
ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
942+
oid, INV_WRITE);
943+
else
944+
ahprintf(AH, "SELECT pg_catalog.lo_open('%u', %d);\n",
945+
oid, INV_WRITE);
935946
}
936947

937948
AH->writingBlob = 1;
@@ -1829,6 +1840,9 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
18291840
AH->vmin = K_VERS_MINOR;
18301841
AH->vrev = K_VERS_REV;
18311842

1843+
/* Make a convenient integer <maj><min><rev>00 */
1844+
AH->version = ((AH->vmaj * 256 + AH->vmin) * 256 + AH->vrev) * 256 + 0;
1845+
18321846
/* initialize for backwards compatible string processing */
18331847
AH->public.encoding = 0; /* PG_SQL_ASCII */
18341848
AH->public.std_strings = false;
@@ -2068,12 +2082,13 @@ ReadToc(ArchiveHandle *AH)
20682082
else
20692083
{
20702084
/*
2071-
* rules for pre-8.4 archives wherein pg_dump hasn't classified
2072-
* the entries into sections
2085+
* Rules for pre-8.4 archives wherein pg_dump hasn't classified
2086+
* the entries into sections. This list need not cover entry
2087+
* types added later than 8.4.
20732088
*/
20742089
if (strcmp(te->desc, "COMMENT") == 0 ||
20752090
strcmp(te->desc, "ACL") == 0 ||
2076-
strcmp(te->desc, "DEFAULT ACL") == 0)
2091+
strcmp(te->desc, "ACL LANGUAGE") == 0)
20772092
te->section = SECTION_NONE;
20782093
else if (strcmp(te->desc, "TABLE DATA") == 0 ||
20792094
strcmp(te->desc, "BLOBS") == 0 ||
@@ -2228,10 +2243,10 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
22282243
return 0;
22292244

22302245
/* If it's an ACL, maybe ignore it */
2231-
if ((!include_acls || ropt->aclsSkip) &&
2232-
(strcmp(te->desc, "ACL") == 0 || strcmp(te->desc, "DEFAULT ACL") == 0))
2246+
if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te))
22332247
return 0;
22342248

2249+
/* Ignore DATABASE entry unless we should create it */
22352250
if (!ropt->create && strcmp(te->desc, "DATABASE") == 0)
22362251
return 0;
22372252

@@ -2286,9 +2301,18 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
22862301
if (!te->hadDumper)
22872302
{
22882303
/*
2289-
* Special Case: If 'SEQUENCE SET' then it is considered a data entry
2304+
* Special Case: If 'SEQUENCE SET' or anything to do with BLOBs,
2305+
* then it is considered a data entry. We don't need to check for
2306+
* the BLOBS entry or old-style BLOB COMMENTS, because they will
2307+
* have hadDumper = true ... but we do need to check new-style
2308+
* BLOB comments.
22902309
*/
2291-
if (strcmp(te->desc, "SEQUENCE SET") == 0)
2310+
if (strcmp(te->desc, "SEQUENCE SET") == 0 ||
2311+
strcmp(te->desc, "BLOB") == 0 ||
2312+
(strcmp(te->desc, "ACL") == 0 &&
2313+
strncmp(te->tag, "LARGE OBJECT ", 13) == 0) ||
2314+
(strcmp(te->desc, "COMMENT") == 0 &&
2315+
strncmp(te->tag, "LARGE OBJECT ", 13) == 0))
22922316
res = res & REQ_DATA;
22932317
else
22942318
res = res & ~REQ_DATA;
@@ -2320,6 +2344,20 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
23202344
return res;
23212345
}
23222346

2347+
/*
2348+
* Identify TOC entries that are ACLs.
2349+
*/
2350+
static bool
2351+
_tocEntryIsACL(TocEntry *te)
2352+
{
2353+
/* "ACL LANGUAGE" was a crock emitted only in PG 7.4 */
2354+
if (strcmp(te->desc, "ACL") == 0 ||
2355+
strcmp(te->desc, "ACL LANGUAGE") == 0 ||
2356+
strcmp(te->desc, "DEFAULT ACL") == 0)
2357+
return true;
2358+
return false;
2359+
}
2360+
23232361
/*
23242362
* Issue SET commands for parameters that we want to have set the same way
23252363
* at all times during execution of a restore script.
@@ -2685,6 +2723,13 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
26852723
return;
26862724
}
26872725

2726+
/* BLOBs just have a name, but it's numeric so must not use fmtId */
2727+
if (strcmp(type, "BLOB") == 0)
2728+
{
2729+
appendPQExpBuffer(buf, "LARGE OBJECT %s", te->tag);
2730+
return;
2731+
}
2732+
26882733
/*
26892734
* These object types require additional decoration. Fortunately, the
26902735
* information needed is exactly what's in the DROP command.
@@ -2723,14 +2768,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
27232768
/* ACLs are dumped only during acl pass */
27242769
if (acl_pass)
27252770
{
2726-
if (!(strcmp(te->desc, "ACL") == 0 ||
2727-
strcmp(te->desc, "DEFAULT ACL") == 0))
2771+
if (!_tocEntryIsACL(te))
27282772
return;
27292773
}
27302774
else
27312775
{
2732-
if (strcmp(te->desc, "ACL") == 0 ||
2733-
strcmp(te->desc, "DEFAULT ACL") == 0)
2776+
if (_tocEntryIsACL(te))
27342777
return;
27352778
}
27362779

@@ -2824,6 +2867,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
28242867
strlen(te->owner) > 0 && strlen(te->dropStmt) > 0)
28252868
{
28262869
if (strcmp(te->desc, "AGGREGATE") == 0 ||
2870+
strcmp(te->desc, "BLOB") == 0 ||
28272871
strcmp(te->desc, "CONVERSION") == 0 ||
28282872
strcmp(te->desc, "DATABASE") == 0 ||
28292873
strcmp(te->desc, "DOMAIN") == 0 ||
@@ -2873,7 +2917,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
28732917
* If it's an ACL entry, it might contain SET SESSION AUTHORIZATION
28742918
* commands, so we can no longer assume we know the current auth setting.
28752919
*/
2876-
if (strncmp(te->desc, "ACL", 3) == 0)
2920+
if (acl_pass)
28772921
{
28782922
if (AH->currUser)
28792923
free(AH->currUser);

src/bin/pg_dump/pg_backup_archiver.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*
1818
*
1919
* IDENTIFICATION
20-
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.83 2009/12/14 00:39:11 itagaki Exp $
20+
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.h,v 1.84 2010/02/18 01:29:10 tgl Exp $
2121
*
2222
*-------------------------------------------------------------------------
2323
*/
@@ -61,16 +61,16 @@ typedef struct _z_stream
6161
typedef z_stream *z_streamp;
6262
#endif
6363

64+
/* Current archive version number (the format we can output) */
6465
#define K_VERS_MAJOR 1
65-
#define K_VERS_MINOR 11
66+
#define K_VERS_MINOR 12
6667
#define K_VERS_REV 0
6768

6869
/* Data block types */
6970
#define BLK_DATA 1
70-
#define BLK_BLOB 2
7171
#define BLK_BLOBS 3
7272

73-
/* Some important version numbers (checked in code) */
73+
/* Historical version numbers (checked in code) */
7474
#define K_VERS_1_0 (( (1 * 256 + 0) * 256 + 0) * 256 + 0)
7575
#define K_VERS_1_2 (( (1 * 256 + 2) * 256 + 0) * 256 + 0) /* Allow No ZLIB */
7676
#define K_VERS_1_3 (( (1 * 256 + 3) * 256 + 0) * 256 + 0) /* BLOBs */
@@ -87,8 +87,11 @@ typedef z_stream *z_streamp;
8787
#define K_VERS_1_10 (( (1 * 256 + 10) * 256 + 0) * 256 + 0) /* add tablespace */
8888
#define K_VERS_1_11 (( (1 * 256 + 11) * 256 + 0) * 256 + 0) /* add toc section
8989
* indicator */
90+
#define K_VERS_1_12 (( (1 * 256 + 12) * 256 + 0) * 256 + 0) /* add separate BLOB
91+
* entries */
9092

91-
#define K_VERS_MAX (( (1 * 256 + 11) * 256 + 255) * 256 + 0)
93+
/* Newest format we can read */
94+
#define K_VERS_MAX (( (1 * 256 + 12) * 256 + 255) * 256 + 0)
9295

9396

9497
/* Flags to indicate disposition of offsets stored in files */

src/bin/pg_dump/pg_backup_db.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Implements the basic DB functions used by the archiver.
66
*
77
* IDENTIFICATION
8-
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.87 2010/02/17 04:19:40 tgl Exp $
8+
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_db.c,v 1.88 2010/02/18 01:29:10 tgl Exp $
99
*
1010
*-------------------------------------------------------------------------
1111
*/
@@ -703,16 +703,26 @@ CommitTransaction(ArchiveHandle *AH)
703703
void
704704
DropBlobIfExists(ArchiveHandle *AH, Oid oid)
705705
{
706-
/* Call lo_unlink only if exists to avoid not-found error. */
707-
if (PQserverVersion(AH->connection) >= 90000)
706+
/*
707+
* If we are not restoring to a direct database connection, we have to
708+
* guess about how to detect whether the blob exists. Assume new-style.
709+
*/
710+
if (AH->connection == NULL ||
711+
PQserverVersion(AH->connection) >= 90000)
708712
{
709-
ahprintf(AH, "SELECT pg_catalog.lo_unlink(oid) "
710-
"FROM pg_catalog.pg_largeobject_metadata "
711-
"WHERE oid = %u;\n", oid);
713+
ahprintf(AH,
714+
"SELECT pg_catalog.lo_unlink(oid) "
715+
"FROM pg_catalog.pg_largeobject_metadata "
716+
"WHERE oid = '%u';\n",
717+
oid);
712718
}
713719
else
714720
{
715-
ahprintf(AH, "SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u') THEN pg_catalog.lo_unlink('%u') END;\n",
721+
/* Restoring to pre-9.0 server, so do it the old way */
722+
ahprintf(AH,
723+
"SELECT CASE WHEN EXISTS("
724+
"SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u'"
725+
") THEN pg_catalog.lo_unlink('%u') END;\n",
716726
oid, oid);
717727
}
718728
}

src/bin/pg_dump/pg_backup_null.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*
1818
*
1919
* IDENTIFICATION
20-
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.23 2009/12/14 00:39:11 itagaki Exp $
20+
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_null.c,v 1.24 2010/02/18 01:29:10 tgl Exp $
2121
*
2222
*-------------------------------------------------------------------------
2323
*/
@@ -147,14 +147,21 @@ _StartBlobs(ArchiveHandle *AH, TocEntry *te)
147147
static void
148148
_StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
149149
{
150+
bool old_blob_style = (AH->version < K_VERS_1_12);
151+
150152
if (oid == 0)
151153
die_horribly(AH, NULL, "invalid OID for large object\n");
152154

153-
if (AH->ropt->dropSchema)
155+
/* With an old archive we must do drop and create logic here */
156+
if (old_blob_style && AH->ropt->dropSchema)
154157
DropBlobIfExists(AH, oid);
155158

156-
ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
157-
oid, INV_WRITE);
159+
if (old_blob_style)
160+
ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
161+
oid, INV_WRITE);
162+
else
163+
ahprintf(AH, "SELECT pg_catalog.lo_open('%u', %d);\n",
164+
oid, INV_WRITE);
158165

159166
AH->WriteDataPtr = _WriteBlobData;
160167
}

0 commit comments

Comments
 (0)