diff -cprN head/doc/src/sgml/catalogs.sgml work/doc/src/sgml/catalogs.sgml
*** head/doc/src/sgml/catalogs.sgml	Fri Dec 11 12:39:49 2009
--- work/doc/src/sgml/catalogs.sgml	Fri Dec 11 16:38:25 2009
***************
*** 3136,3142 ****
  
    <para>
     <structname>pg_largeobject</structname> should not be readable by the
!    public, since the catalog contains data in large objects of all users.
     <structname>pg_largeobject_metadata</> is a publicly readable catalog
     that only contains identifiers of large objects.
    </para>
--- 3136,3143 ----
  
    <para>
     <structname>pg_largeobject</structname> should not be readable by the
!    public (except for <structfield>loid</structfield>), since the catalog
!    contains data in large objects of all users.
     <structname>pg_largeobject_metadata</> is a publicly readable catalog
     that only contains identifiers of large objects.
    </para>
Binary files head/dump.dmp and work/dump.dmp differ
diff -cprN head/src/bin/initdb/initdb.c work/src/bin/initdb/initdb.c
*** head/src/bin/initdb/initdb.c	Fri Dec 11 12:39:49 2009
--- work/src/bin/initdb/initdb.c	Fri Dec 11 16:38:25 2009
*************** setup_privileges(void)
*** 1784,1789 ****
--- 1784,1790 ----
  		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n",
  		"GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n",
  		"REVOKE ALL ON pg_largeobject FROM PUBLIC;\n",
+ 		"GRANT SELECT (loid) ON pg_largeobject TO PUBLIC;\n",
  		NULL
  	};
  
diff -cprN head/src/bin/pg_dump/pg_backup_archiver.c work/src/bin/pg_dump/pg_backup_archiver.c
*** head/src/bin/pg_dump/pg_backup_archiver.c	Tue Oct  6 16:37:35 2009
--- work/src/bin/pg_dump/pg_backup_archiver.c	Fri Dec 11 17:03:56 2009
*************** StartRestoreBlob(ArchiveHandle *AH, Oid 
*** 914,921 ****
  	ahlog(AH, 2, "restoring large object with OID %u\n", oid);
  
  	if (drop)
! 		ahprintf(AH, "SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u') THEN pg_catalog.lo_unlink('%u') END;\n",
! 				 oid, oid);
  
  	if (AH->connection)
  	{
--- 914,920 ----
  	ahlog(AH, 2, "restoring large object with OID %u\n", oid);
  
  	if (drop)
! 		DropBlobIfExists(AH, oid);
  
  	if (AH->connection)
  	{
diff -cprN head/src/bin/pg_dump/pg_backup_archiver.h work/src/bin/pg_dump/pg_backup_archiver.h
*** head/src/bin/pg_dump/pg_backup_archiver.h	Mon Aug 10 09:40:51 2009
--- work/src/bin/pg_dump/pg_backup_archiver.h	Fri Dec 11 16:56:26 2009
*************** extern void InitArchiveFmt_Tar(ArchiveHa
*** 371,376 ****
--- 371,377 ----
  extern bool isValidTarHeader(char *header);
  
  extern int	ReconnectToServer(ArchiveHandle *AH, const char *dbname, const char *newUser);
+ extern void	DropBlobIfExists(ArchiveHandle *AH, Oid oid);
  
  int			ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH);
  int			ahprintf(ArchiveHandle *AH, const char *fmt,...) __attribute__((format(printf, 2, 3)));
diff -cprN head/src/bin/pg_dump/pg_backup_db.c work/src/bin/pg_dump/pg_backup_db.c
*** head/src/bin/pg_dump/pg_backup_db.c	Fri Jun 12 09:52:43 2009
--- work/src/bin/pg_dump/pg_backup_db.c	Fri Dec 11 16:57:35 2009
*************** CommitTransaction(ArchiveHandle *AH)
*** 652,657 ****
--- 652,679 ----
  	ExecuteSqlCommand(AH, "COMMIT", "could not commit database transaction");
  }
  
+ void
+ DropBlobIfExists(ArchiveHandle *AH, Oid oid)
+ {
+ 	const char *lo_relname;
+ 	const char *lo_colname;
+ 
+ 	if (PQserverVersion(AH->connection) >= 80500)
+ 	{
+ 		lo_relname = "pg_largeobject_metadata";
+ 		lo_colname = "oid";
+ 	}
+ 	else
+ 	{
+ 		lo_relname = "pg_largeobject";
+ 		lo_colname = "loid";
+ 	}
+ 
+ 	/* Call lo_unlink only if exists to avoid not-found error. */
+ 	ahprintf(AH, "SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.%s WHERE %s = '%u') THEN pg_catalog.lo_unlink('%u') END;\n",
+ 			 lo_relname, lo_colname, oid, oid);
+ }
+ 
  static bool
  _isIdentChar(unsigned char c)
  {
diff -cprN head/src/bin/pg_dump/pg_backup_null.c work/src/bin/pg_dump/pg_backup_null.c
*** head/src/bin/pg_dump/pg_backup_null.c	Wed Aug  5 10:11:11 2009
--- work/src/bin/pg_dump/pg_backup_null.c	Fri Dec 11 16:58:30 2009
*************** _StartBlob(ArchiveHandle *AH, TocEntry *
*** 151,158 ****
  		die_horribly(AH, NULL, "invalid OID for large object\n");
  
  	if (AH->ropt->dropSchema)
! 		ahprintf(AH, "SELECT CASE WHEN EXISTS(SELECT 1 FROM pg_catalog.pg_largeobject WHERE loid = '%u') THEN pg_catalog.lo_unlink('%u') END;\n",
! 				 oid, oid);
  
  	ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
  			 oid, INV_WRITE);
--- 151,157 ----
  		die_horribly(AH, NULL, "invalid OID for large object\n");
  
  	if (AH->ropt->dropSchema)
! 		DropBlobIfExists(AH, oid);
  
  	ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
  			 oid, INV_WRITE);
diff -cprN head/src/bin/pg_dump/pg_dump.c work/src/bin/pg_dump/pg_dump.c
*** head/src/bin/pg_dump/pg_dump.c	Fri Dec 11 12:39:49 2009
--- work/src/bin/pg_dump/pg_dump.c	Fri Dec 11 16:38:26 2009
*************** hasBlobs(Archive *AH)
*** 1945,1951 ****
  	selectSourceSchema("pg_catalog");
  
  	/* Check for BLOB OIDs */
! 	if (AH->remoteVersion >= 70100)
  		blobQry = "SELECT loid FROM pg_largeobject LIMIT 1";
  	else
  		blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1";
--- 1945,1953 ----
  	selectSourceSchema("pg_catalog");
  
  	/* Check for BLOB OIDs */
! 	if (AH->remoteVersion >= 80500)
! 		blobQry = "SELECT oid FROM pg_largeobject_metadata LIMIT 1";
! 	else if (AH->remoteVersion >= 70100)
  		blobQry = "SELECT loid FROM pg_largeobject LIMIT 1";
  	else
  		blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1";
*************** dumpBlobs(Archive *AH, void *arg)
*** 1981,1987 ****
  	selectSourceSchema("pg_catalog");
  
  	/* Cursor to get all BLOB OIDs */
! 	if (AH->remoteVersion >= 70100)
  		blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject";
  	else
  		blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'";
--- 1983,1991 ----
  	selectSourceSchema("pg_catalog");
  
  	/* Cursor to get all BLOB OIDs */
! 	if (AH->remoteVersion >= 80500)
! 		blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_largeobject_metadata";
! 	else if (AH->remoteVersion >= 70100)
  		blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject";
  	else
  		blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'";
diff -cprN head/src/test/regress/expected/privileges.out work/src/test/regress/expected/privileges.out
*** head/src/test/regress/expected/privileges.out	Fri Dec 11 12:39:49 2009
--- work/src/test/regress/expected/privileges.out	Fri Dec 11 16:38:25 2009
*************** SELECT lo_unlink(1002);
*** 1041,1046 ****
--- 1041,1061 ----
  SELECT lo_export(1001, '/dev/null');			-- to be denied
  ERROR:  must be superuser to use server-side lo_export()
  HINT:  Anyone can use the client-side lo_export() provided by libpq.
+ -- don't allow unpriv users to access pg_largeobject contents
+ \c -
+ SELECT * FROM pg_largeobject LIMIT 0;
+  loid | pageno | data 
+ ------+--------+------
+ (0 rows)
+ 
+ SET SESSION AUTHORIZATION regressuser1;
+ SELECT * FROM pg_largeobject LIMIT 0;			-- to be denied
+ ERROR:  permission denied for relation pg_largeobject
+ SELECT loid FROM pg_largeobject LIMIT 0;
+  loid 
+ ------
+ (0 rows)
+ 
  -- test default ACLs
  \c -
  CREATE SCHEMA testns;
diff -cprN head/src/test/regress/sql/privileges.sql work/src/test/regress/sql/privileges.sql
*** head/src/test/regress/sql/privileges.sql	Fri Dec 11 12:39:49 2009
--- work/src/test/regress/sql/privileges.sql	Fri Dec 11 16:38:25 2009
*************** SELECT lo_truncate(lo_open(1002, x'20000
*** 565,570 ****
--- 565,578 ----
  SELECT lo_unlink(1002);
  SELECT lo_export(1001, '/dev/null');			-- to be denied
  
+ -- don't allow unpriv users to access pg_largeobject contents
+ \c -
+ SELECT * FROM pg_largeobject LIMIT 0;
+ 
+ SET SESSION AUTHORIZATION regressuser1;
+ SELECT * FROM pg_largeobject LIMIT 0;			-- to be denied
+ SELECT loid FROM pg_largeobject LIMIT 0;
+ 
  -- test default ACLs
  \c -
  