2600 lines
80 KiB
Diff
2600 lines
80 KiB
Diff
diff -Nrpc base/contrib/lo/lo_test.sql blob/contrib/lo/lo_test.sql
|
|
*** base/contrib/lo/lo_test.sql Sat Nov 17 20:15:40 2007
|
|
--- blob/contrib/lo/lo_test.sql Fri Dec 18 09:40:55 2009
|
|
*************** SET search_path = public;
|
|
*** 12,18 ****
|
|
--
|
|
|
|
-- Check what is in pg_largeobject
|
|
! SELECT count(DISTINCT loid) FROM pg_largeobject;
|
|
|
|
-- ignore any errors here - simply drop the table if it already exists
|
|
DROP TABLE a;
|
|
--- 12,18 ----
|
|
--
|
|
|
|
-- Check what is in pg_largeobject
|
|
! SELECT count(oid) FROM pg_largeobject_metadata;
|
|
|
|
-- ignore any errors here - simply drop the table if it already exists
|
|
DROP TABLE a;
|
|
*************** DELETE FROM a;
|
|
*** 74,79 ****
|
|
DROP TABLE a;
|
|
|
|
-- Check what is in pg_largeobject ... if different from original, trouble
|
|
! SELECT count(DISTINCT loid) FROM pg_largeobject;
|
|
|
|
-- end of tests
|
|
--- 74,79 ----
|
|
DROP TABLE a;
|
|
|
|
-- Check what is in pg_largeobject ... if different from original, trouble
|
|
! SELECT count(oid) FROM pg_largeobject_metadata;
|
|
|
|
-- end of tests
|
|
diff -Nrpc base/contrib/vacuumlo/vacuumlo.c blob/contrib/vacuumlo/vacuumlo.c
|
|
*** base/contrib/vacuumlo/vacuumlo.c Mon Mar 2 13:43:07 2009
|
|
--- blob/contrib/vacuumlo/vacuumlo.c Fri Dec 18 09:40:55 2009
|
|
*************** vacuumlo(char *database, struct _param *
|
|
*** 142,148 ****
|
|
*/
|
|
buf[0] = '\0';
|
|
strcat(buf, "CREATE TEMP TABLE vacuum_l AS ");
|
|
! strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject ");
|
|
res = PQexec(conn, buf);
|
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
{
|
|
--- 142,151 ----
|
|
*/
|
|
buf[0] = '\0';
|
|
strcat(buf, "CREATE TEMP TABLE vacuum_l AS ");
|
|
! if (PQserverVersion(conn) >= 80500)
|
|
! strcat(buf, "SELECT oid AS lo FROM pg_largeobject_metadata");
|
|
! else
|
|
! strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject");
|
|
res = PQexec(conn, buf);
|
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
|
{
|
|
diff -Nrpc base/src/backend/catalog/Makefile blob/src/backend/catalog/Makefile
|
|
*** base/src/backend/catalog/Makefile Wed May 13 11:30:07 2009
|
|
--- blob/src/backend/catalog/Makefile Fri Dec 18 09:40:55 2009
|
|
*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr
|
|
*** 29,37 ****
|
|
pg_proc.h pg_type.h pg_attribute.h pg_class.h \
|
|
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
|
|
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
|
|
! pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
|
|
! pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \
|
|
! pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
|
|
pg_database.h pg_tablespace.h pg_pltemplate.h \
|
|
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
|
|
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
|
|
--- 29,37 ----
|
|
pg_proc.h pg_type.h pg_attribute.h pg_class.h \
|
|
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
|
|
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
|
|
! pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
|
|
! pg_statistic.h pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h \
|
|
! pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
|
|
pg_database.h pg_tablespace.h pg_pltemplate.h \
|
|
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
|
|
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
|
|
diff -Nrpc base/src/backend/catalog/aclchk.c blob/src/backend/catalog/aclchk.c
|
|
*** base/src/backend/catalog/aclchk.c Thu Mar 18 01:40:54 2010
|
|
--- blob/src/backend/catalog/aclchk.c Thu Mar 18 09:43:03 2010
|
|
***************
|
|
*** 30,35 ****
|
|
--- 30,37 ----
|
|
#include "catalog/pg_foreign_data_wrapper.h"
|
|
#include "catalog/pg_foreign_server.h"
|
|
#include "catalog/pg_language.h"
|
|
+ #include "catalog/pg_largeobject.h"
|
|
+ #include "catalog/pg_largeobject_metadata.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_opclass.h"
|
|
#include "catalog/pg_operator.h"
|
|
*************** static void ExecGrant_Fdw(InternalGrant
|
|
*** 57,62 ****
|
|
--- 59,65 ----
|
|
static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
|
|
static void ExecGrant_Function(InternalGrant *grantStmt);
|
|
static void ExecGrant_Language(InternalGrant *grantStmt);
|
|
+ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
|
|
static void ExecGrant_Namespace(InternalGrant *grantStmt);
|
|
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
|
|
|
|
*************** restrict_and_check_grant(bool is_grant,
|
|
*** 200,205 ****
|
|
--- 203,211 ----
|
|
case ACL_KIND_LANGUAGE:
|
|
whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
|
|
break;
|
|
+ case ACL_KIND_LARGEOBJECT:
|
|
+ whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
+ break;
|
|
case ACL_KIND_NAMESPACE:
|
|
whole_mask = ACL_ALL_RIGHTS_NAMESPACE;
|
|
break;
|
|
*************** ExecuteGrantStmt(GrantStmt *stmt)
|
|
*** 380,385 ****
|
|
--- 386,395 ----
|
|
all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
|
|
errormsg = gettext_noop("invalid privilege type %s for language");
|
|
break;
|
|
+ case ACL_OBJECT_LARGEOBJECT:
|
|
+ all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
+ errormsg = gettext_noop("invalid privilege type %s for large object");
|
|
+ break;
|
|
case ACL_OBJECT_NAMESPACE:
|
|
all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
|
|
errormsg = gettext_noop("invalid privilege type %s for schema");
|
|
*************** ExecGrantStmt_oids(InternalGrant *istmt)
|
|
*** 485,490 ****
|
|
--- 495,503 ----
|
|
case ACL_OBJECT_LANGUAGE:
|
|
ExecGrant_Language(istmt);
|
|
break;
|
|
+ case ACL_OBJECT_LARGEOBJECT:
|
|
+ ExecGrant_Largeobject(istmt);
|
|
+ break;
|
|
case ACL_OBJECT_NAMESPACE:
|
|
ExecGrant_Namespace(istmt);
|
|
break;
|
|
*************** objectNamesToOids(GrantObjectType objtyp
|
|
*** 569,574 ****
|
|
--- 582,601 ----
|
|
ReleaseSysCache(tuple);
|
|
}
|
|
break;
|
|
+ case ACL_OBJECT_LARGEOBJECT:
|
|
+ foreach(cell, objnames)
|
|
+ {
|
|
+ Oid lobjOid = intVal(lfirst(cell));
|
|
+
|
|
+ if (!LargeObjectExists(lobjOid))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
+ errmsg("large object %u does not exist",
|
|
+ lobjOid)));
|
|
+
|
|
+ objects = lappend_oid(objects, lobjOid);
|
|
+ }
|
|
+ break;
|
|
case ACL_OBJECT_NAMESPACE:
|
|
foreach(cell, objnames)
|
|
{
|
|
*************** ExecGrant_Language(InternalGrant *istmt)
|
|
*** 1782,1787 ****
|
|
--- 1809,1946 ----
|
|
}
|
|
|
|
static void
|
|
+ ExecGrant_Largeobject(InternalGrant *istmt)
|
|
+ {
|
|
+ Relation relation;
|
|
+ ListCell *cell;
|
|
+
|
|
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
+ istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
+
|
|
+ relation = heap_open(LargeObjectMetadataRelationId,
|
|
+ RowExclusiveLock);
|
|
+
|
|
+ foreach(cell, istmt->objects)
|
|
+ {
|
|
+ Oid loid = lfirst_oid(cell);
|
|
+ Form_pg_largeobject_metadata form_lo_meta;
|
|
+ char loname[NAMEDATALEN];
|
|
+ Datum aclDatum;
|
|
+ bool isNull;
|
|
+ AclMode avail_goptions;
|
|
+ AclMode this_privileges;
|
|
+ Acl *old_acl;
|
|
+ Acl *new_acl;
|
|
+ Oid grantorId;
|
|
+ Oid ownerId;
|
|
+ HeapTuple newtuple;
|
|
+ Datum values[Natts_pg_largeobject_metadata];
|
|
+ bool nulls[Natts_pg_largeobject_metadata];
|
|
+ bool replaces[Natts_pg_largeobject_metadata];
|
|
+ int noldmembers;
|
|
+ int nnewmembers;
|
|
+ Oid *oldmembers;
|
|
+ Oid *newmembers;
|
|
+ ScanKeyData entry[1];
|
|
+ SysScanDesc scan;
|
|
+ HeapTuple tuple;
|
|
+
|
|
+ /* There's no syscache for pg_largeobject_metadata */
|
|
+ ScanKeyInit(&entry[0],
|
|
+ ObjectIdAttributeNumber,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(loid));
|
|
+
|
|
+ scan = systable_beginscan(relation,
|
|
+ LargeObjectMetadataOidIndexId, true,
|
|
+ SnapshotNow, 1, entry);
|
|
+
|
|
+ tuple = systable_getnext(scan);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for large object %u", loid);
|
|
+
|
|
+ form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple);
|
|
+
|
|
+ /*
|
|
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
+ * substitute the proper default.
|
|
+ */
|
|
+ ownerId = form_lo_meta->lomowner;
|
|
+ aclDatum = heap_getattr(tuple,
|
|
+ Anum_pg_largeobject_metadata_lomacl,
|
|
+ RelationGetDescr(relation), &isNull);
|
|
+ if (isNull)
|
|
+ old_acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId);
|
|
+ else
|
|
+ old_acl = DatumGetAclPCopy(aclDatum);
|
|
+
|
|
+ /* Determine ID to do the grant as, and available grant options */
|
|
+ select_best_grantor(GetUserId(), istmt->privileges,
|
|
+ old_acl, ownerId,
|
|
+ &grantorId, &avail_goptions);
|
|
+
|
|
+ /*
|
|
+ * Restrict the privileges to what we can actually grant, and emit the
|
|
+ * standards-mandated warning and error messages.
|
|
+ */
|
|
+ snprintf(loname, sizeof(loname), "large object %u", loid);
|
|
+ this_privileges =
|
|
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
+ istmt->all_privs, istmt->privileges,
|
|
+ loid, grantorId, ACL_KIND_LARGEOBJECT,
|
|
+ loname, 0, NULL);
|
|
+
|
|
+ /*
|
|
+ * Generate new ACL.
|
|
+ *
|
|
+ * We need the members of both old and new ACLs so we can correct the
|
|
+ * shared dependency information.
|
|
+ */
|
|
+ noldmembers = aclmembers(old_acl, &oldmembers);
|
|
+
|
|
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
+ istmt->grant_option, istmt->behavior,
|
|
+ istmt->grantees, this_privileges,
|
|
+ grantorId, ownerId);
|
|
+
|
|
+ nnewmembers = aclmembers(new_acl, &newmembers);
|
|
+
|
|
+ /* finished building new ACL value, now insert it */
|
|
+ MemSet(values, 0, sizeof(values));
|
|
+ MemSet(nulls, false, sizeof(nulls));
|
|
+ MemSet(replaces, false, sizeof(replaces));
|
|
+
|
|
+ replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
|
|
+ values[Anum_pg_largeobject_metadata_lomacl - 1]
|
|
+ = PointerGetDatum(new_acl);
|
|
+
|
|
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
|
|
+ values, nulls, replaces);
|
|
+
|
|
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
+
|
|
+ /* keep the catalog indexes up to date */
|
|
+ CatalogUpdateIndexes(relation, newtuple);
|
|
+
|
|
+ /* Update the shared dependency ACL info */
|
|
+ updateAclDependencies(LargeObjectRelationId,
|
|
+ HeapTupleGetOid(tuple), 0,
|
|
+ ownerId, istmt->is_grant,
|
|
+ noldmembers, oldmembers,
|
|
+ nnewmembers, newmembers);
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ pfree(new_acl);
|
|
+
|
|
+ /* prevent error when processing duplicate objects */
|
|
+ CommandCounterIncrement();
|
|
+ }
|
|
+
|
|
+ heap_close(relation, RowExclusiveLock);
|
|
+ }
|
|
+
|
|
+ static void
|
|
ExecGrant_Namespace(InternalGrant *istmt)
|
|
{
|
|
Relation relation;
|
|
*************** static const char *const no_priv_msg[MAX
|
|
*** 2121,2126 ****
|
|
--- 2280,2287 ----
|
|
gettext_noop("permission denied for type %s"),
|
|
/* ACL_KIND_LANGUAGE */
|
|
gettext_noop("permission denied for language %s"),
|
|
+ /* ACL_KIND_LARGEOBJECT */
|
|
+ gettext_noop("permission denied for large object %s"),
|
|
/* ACL_KIND_NAMESPACE */
|
|
gettext_noop("permission denied for schema %s"),
|
|
/* ACL_KIND_OPCLASS */
|
|
*************** static const char *const not_owner_msg[M
|
|
*** 2159,2164 ****
|
|
--- 2320,2327 ----
|
|
gettext_noop("must be owner of type %s"),
|
|
/* ACL_KIND_LANGUAGE */
|
|
gettext_noop("must be owner of language %s"),
|
|
+ /* ACL_KIND_LARGEOBJECT */
|
|
+ gettext_noop("must be owner of large object %s"),
|
|
/* ACL_KIND_NAMESPACE */
|
|
gettext_noop("must be owner of schema %s"),
|
|
/* ACL_KIND_OPCLASS */
|
|
*************** pg_aclmask(AclObjectKind objkind, Oid ta
|
|
*** 2278,2283 ****
|
|
--- 2441,2449 ----
|
|
return pg_proc_aclmask(table_oid, roleid, mask, how);
|
|
case ACL_KIND_LANGUAGE:
|
|
return pg_language_aclmask(table_oid, roleid, mask, how);
|
|
+ case ACL_KIND_LARGEOBJECT:
|
|
+ return pg_largeobject_aclmask_snapshot(table_oid, roleid,
|
|
+ mask, how, SnapshotNow);
|
|
case ACL_KIND_NAMESPACE:
|
|
return pg_namespace_aclmask(table_oid, roleid, mask, how);
|
|
case ACL_KIND_TABLESPACE:
|
|
*************** pg_language_aclmask(Oid lang_oid, Oid ro
|
|
*** 2661,2666 ****
|
|
--- 2827,2916 ----
|
|
}
|
|
|
|
/*
|
|
+ * Exported routine for examining a user's privileges for a largeobject
|
|
+ *
|
|
+ * The reason why this interface has an argument of snapshot is that
|
|
+ * we apply a snapshot available on lo_open(), not SnapshotNow, when
|
|
+ * it is opened as read-only mode.
|
|
+ * If we could see the metadata and data from inconsistent viewpoint,
|
|
+ * it will give us much confusion. So, we need to provide an interface
|
|
+ * which takes an argument of snapshot.
|
|
+ *
|
|
+ * If the caller refers a large object with a certain snapshot except
|
|
+ * for SnapshotNow, its permission checks should be also applied in
|
|
+ * the same snapshot.
|
|
+ */
|
|
+ AclMode
|
|
+ pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
|
|
+ AclMode mask, AclMaskHow how,
|
|
+ Snapshot snapshot)
|
|
+ {
|
|
+ AclMode result;
|
|
+ Relation pg_lo_meta;
|
|
+ ScanKeyData entry[1];
|
|
+ SysScanDesc scan;
|
|
+ HeapTuple tuple;
|
|
+ Datum aclDatum;
|
|
+ bool isNull;
|
|
+ Acl *acl;
|
|
+ Oid ownerId;
|
|
+
|
|
+ /* Superusers bypass all permission checking. */
|
|
+ if (superuser_arg(roleid))
|
|
+ return mask;
|
|
+
|
|
+ /*
|
|
+ * Get the largeobject's ACL from pg_language_metadata
|
|
+ */
|
|
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
|
+ AccessShareLock);
|
|
+
|
|
+ ScanKeyInit(&entry[0],
|
|
+ ObjectIdAttributeNumber,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(lobj_oid));
|
|
+
|
|
+ scan = systable_beginscan(pg_lo_meta,
|
|
+ LargeObjectMetadataOidIndexId, true,
|
|
+ snapshot, 1, entry);
|
|
+
|
|
+ tuple = systable_getnext(scan);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
+ errmsg("large object %u does not exist", lobj_oid)));
|
|
+
|
|
+ ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
|
|
+
|
|
+ aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl,
|
|
+ RelationGetDescr(pg_lo_meta), &isNull);
|
|
+
|
|
+ if (isNull)
|
|
+ {
|
|
+ /* No ACL, so build default ACL */
|
|
+ acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId);
|
|
+ aclDatum = (Datum) 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* detoast ACL if necessary */
|
|
+ acl = DatumGetAclP(aclDatum);
|
|
+ }
|
|
+
|
|
+ result = aclmask(acl, roleid, ownerId, mask, how);
|
|
+
|
|
+ /* if we have a detoasted copy, free it */
|
|
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
+ pfree(acl);
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(pg_lo_meta, AccessShareLock);
|
|
+
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ /*
|
|
* Exported routine for examining a user's privileges for a namespace
|
|
*/
|
|
AclMode
|
|
*************** pg_language_aclcheck(Oid lang_oid, Oid r
|
|
*** 3111,3116 ****
|
|
--- 3361,3380 ----
|
|
}
|
|
|
|
/*
|
|
+ * Exported routine for checking a user's access privileges to a largeobject
|
|
+ */
|
|
+ AclResult
|
|
+ pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode,
|
|
+ Snapshot snapshot)
|
|
+ {
|
|
+ if (pg_largeobject_aclmask_snapshot(lobj_oid, roleid, mode,
|
|
+ ACLMASK_ANY, snapshot) != 0)
|
|
+ return ACLCHECK_OK;
|
|
+ else
|
|
+ return ACLCHECK_NO_PRIV;
|
|
+ }
|
|
+
|
|
+ /*
|
|
* Exported routine for checking a user's access privileges to a namespace
|
|
*/
|
|
AclResult
|
|
*************** pg_language_ownercheck(Oid lan_oid, Oid
|
|
*** 3301,3306 ****
|
|
--- 3565,3617 ----
|
|
}
|
|
|
|
/*
|
|
+ * Ownership check for a largeobject (specified by OID)
|
|
+ *
|
|
+ * Note that we have no candidate to call this routine with a certain
|
|
+ * snapshot except for SnapshotNow, so we don't provide an interface
|
|
+ * with _snapshot() version now.
|
|
+ */
|
|
+ bool
|
|
+ pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid)
|
|
+ {
|
|
+ Relation pg_lo_meta;
|
|
+ ScanKeyData entry[1];
|
|
+ SysScanDesc scan;
|
|
+ HeapTuple tuple;
|
|
+ Oid ownerId;
|
|
+
|
|
+ /* Superusers bypass all permission checking. */
|
|
+ if (superuser_arg(roleid))
|
|
+ return true;
|
|
+
|
|
+ /* There's no syscache for pg_largeobject_metadata */
|
|
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
|
+ AccessShareLock);
|
|
+
|
|
+ ScanKeyInit(&entry[0],
|
|
+ ObjectIdAttributeNumber,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(lobj_oid));
|
|
+
|
|
+ scan = systable_beginscan(pg_lo_meta,
|
|
+ LargeObjectMetadataOidIndexId, true,
|
|
+ SnapshotNow, 1, entry);
|
|
+
|
|
+ tuple = systable_getnext(scan);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
+ errmsg("large object %u does not exist", lobj_oid)));
|
|
+
|
|
+ ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
|
|
+
|
|
+ systable_endscan(scan);
|
|
+ heap_close(pg_lo_meta, AccessShareLock);
|
|
+
|
|
+ return has_privs_of_role(roleid, ownerId);
|
|
+ }
|
|
+
|
|
+ /*
|
|
* Ownership check for a namespace (specified by OID).
|
|
*/
|
|
bool
|
|
diff -Nrpc base/src/backend/catalog/dependency.c blob/src/backend/catalog/dependency.c
|
|
*** base/src/backend/catalog/dependency.c Tue Dec 15 17:16:51 2009
|
|
--- blob/src/backend/catalog/dependency.c Fri Dec 18 09:40:55 2009
|
|
***************
|
|
*** 36,41 ****
|
|
--- 36,42 ----
|
|
#include "catalog/pg_foreign_data_wrapper.h"
|
|
#include "catalog/pg_foreign_server.h"
|
|
#include "catalog/pg_language.h"
|
|
+ #include "catalog/pg_largeobject.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_opclass.h"
|
|
#include "catalog/pg_operator.h"
|
|
*************** static const Oid object_classes[MAX_OCLA
|
|
*** 129,134 ****
|
|
--- 130,136 ----
|
|
ConversionRelationId, /* OCLASS_CONVERSION */
|
|
AttrDefaultRelationId, /* OCLASS_DEFAULT */
|
|
LanguageRelationId, /* OCLASS_LANGUAGE */
|
|
+ LargeObjectRelationId, /* OCLASS_LARGEOBJECT */
|
|
OperatorRelationId, /* OCLASS_OPERATOR */
|
|
OperatorClassRelationId, /* OCLASS_OPCLASS */
|
|
OperatorFamilyRelationId, /* OCLASS_OPFAMILY */
|
|
*************** doDeletion(const ObjectAddress *object)
|
|
*** 1071,1076 ****
|
|
--- 1073,1082 ----
|
|
DropProceduralLanguageById(object->objectId);
|
|
break;
|
|
|
|
+ case OCLASS_LARGEOBJECT:
|
|
+ LargeObjectDrop(object->objectId);
|
|
+ break;
|
|
+
|
|
case OCLASS_OPERATOR:
|
|
RemoveOperatorById(object->objectId);
|
|
break;
|
|
*************** getObjectClass(const ObjectAddress *obje
|
|
*** 1984,1989 ****
|
|
--- 1990,1999 ----
|
|
Assert(object->objectSubId == 0);
|
|
return OCLASS_LANGUAGE;
|
|
|
|
+ case LargeObjectRelationId:
|
|
+ Assert(object->objectSubId == 0);
|
|
+ return OCLASS_LARGEOBJECT;
|
|
+
|
|
case OperatorRelationId:
|
|
Assert(object->objectSubId == 0);
|
|
return OCLASS_OPERATOR;
|
|
*************** getObjectDescription(const ObjectAddress
|
|
*** 2232,2237 ****
|
|
--- 2242,2251 ----
|
|
ReleaseSysCache(langTup);
|
|
break;
|
|
}
|
|
+ case OCLASS_LARGEOBJECT:
|
|
+ appendStringInfo(&buffer, _("large object %u"),
|
|
+ object->objectId);
|
|
+ break;
|
|
|
|
case OCLASS_OPERATOR:
|
|
appendStringInfo(&buffer, _("operator %s"),
|
|
diff -Nrpc base/src/backend/catalog/pg_largeobject.c blob/src/backend/catalog/pg_largeobject.c
|
|
*** base/src/backend/catalog/pg_largeobject.c Sat Jan 3 13:01:35 2009
|
|
--- blob/src/backend/catalog/pg_largeobject.c Fri Dec 18 09:40:55 2009
|
|
***************
|
|
*** 16,23 ****
|
|
--- 16,31 ----
|
|
|
|
#include "access/genam.h"
|
|
#include "access/heapam.h"
|
|
+ #include "access/sysattr.h"
|
|
+ #include "catalog/catalog.h"
|
|
+ #include "catalog/dependency.h"
|
|
#include "catalog/indexing.h"
|
|
+ #include "catalog/pg_authid.h"
|
|
#include "catalog/pg_largeobject.h"
|
|
+ #include "catalog/pg_largeobject_metadata.h"
|
|
+ #include "catalog/toasting.h"
|
|
+ #include "miscadmin.h"
|
|
+ #include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/rel.h"
|
|
***************
|
|
*** 27,139 ****
|
|
/*
|
|
* Create a large object having the given LO identifier.
|
|
*
|
|
! * We do this by inserting an empty first page, so that the object will
|
|
! * appear to exist with size 0. Note that the unique index will reject
|
|
! * an attempt to create a duplicate page.
|
|
*/
|
|
! void
|
|
LargeObjectCreate(Oid loid)
|
|
{
|
|
! Relation pg_largeobject;
|
|
HeapTuple ntup;
|
|
! Datum values[Natts_pg_largeobject];
|
|
! bool nulls[Natts_pg_largeobject];
|
|
! int i;
|
|
|
|
! pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock);
|
|
|
|
/*
|
|
! * Form new tuple
|
|
*/
|
|
! for (i = 0; i < Natts_pg_largeobject; i++)
|
|
! {
|
|
! values[i] = (Datum) NULL;
|
|
! nulls[i] = false;
|
|
! }
|
|
|
|
! i = 0;
|
|
! values[i++] = ObjectIdGetDatum(loid);
|
|
! values[i++] = Int32GetDatum(0);
|
|
! values[i++] = DirectFunctionCall1(byteain,
|
|
! CStringGetDatum(""));
|
|
|
|
! ntup = heap_form_tuple(pg_largeobject->rd_att, values, nulls);
|
|
|
|
! /*
|
|
! * Insert it
|
|
! */
|
|
! simple_heap_insert(pg_largeobject, ntup);
|
|
|
|
! /* Update indexes */
|
|
! CatalogUpdateIndexes(pg_largeobject, ntup);
|
|
|
|
! heap_close(pg_largeobject, RowExclusiveLock);
|
|
|
|
! heap_freetuple(ntup);
|
|
}
|
|
|
|
void
|
|
LargeObjectDrop(Oid loid)
|
|
{
|
|
! bool found = false;
|
|
Relation pg_largeobject;
|
|
ScanKeyData skey[1];
|
|
! SysScanDesc sd;
|
|
HeapTuple tuple;
|
|
|
|
ScanKeyInit(&skey[0],
|
|
! Anum_pg_largeobject_loid,
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
! ObjectIdGetDatum(loid));
|
|
|
|
! pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock);
|
|
|
|
! sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
|
|
! SnapshotNow, 1, skey);
|
|
|
|
! while ((tuple = systable_getnext(sd)) != NULL)
|
|
{
|
|
simple_heap_delete(pg_largeobject, &tuple->t_self);
|
|
- found = true;
|
|
}
|
|
|
|
! systable_endscan(sd);
|
|
|
|
heap_close(pg_largeobject, RowExclusiveLock);
|
|
|
|
! if (!found)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("large object %u does not exist", loid)));
|
|
}
|
|
|
|
bool
|
|
LargeObjectExists(Oid loid)
|
|
{
|
|
bool retval = false;
|
|
- Relation pg_largeobject;
|
|
- ScanKeyData skey[1];
|
|
- SysScanDesc sd;
|
|
|
|
- /*
|
|
- * See if we can find any tuples belonging to the specified LO
|
|
- */
|
|
ScanKeyInit(&skey[0],
|
|
! Anum_pg_largeobject_loid,
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
ObjectIdGetDatum(loid));
|
|
|
|
! pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock);
|
|
|
|
! sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
|
|
SnapshotNow, 1, skey);
|
|
|
|
! if (systable_getnext(sd) != NULL)
|
|
retval = true;
|
|
|
|
systable_endscan(sd);
|
|
|
|
! heap_close(pg_largeobject, AccessShareLock);
|
|
|
|
return retval;
|
|
}
|
|
--- 35,292 ----
|
|
/*
|
|
* Create a large object having the given LO identifier.
|
|
*
|
|
! * We create a new large object by inserting an entry into
|
|
! * pg_largeobject_metadata without any data pages, so that the object
|
|
! * will appear to exist with size 0.
|
|
*/
|
|
! Oid
|
|
LargeObjectCreate(Oid loid)
|
|
{
|
|
! Relation pg_lo_meta;
|
|
HeapTuple ntup;
|
|
! Oid loid_new;
|
|
! Datum values[Natts_pg_largeobject_metadata];
|
|
! bool nulls[Natts_pg_largeobject_metadata];
|
|
|
|
! pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
|
! RowExclusiveLock);
|
|
|
|
/*
|
|
! * Insert metadata of the largeobject
|
|
*/
|
|
! memset(values, 0, sizeof(values));
|
|
! memset(nulls, false, sizeof(nulls));
|
|
|
|
! values[Anum_pg_largeobject_metadata_lomowner - 1]
|
|
! = ObjectIdGetDatum(GetUserId());
|
|
! nulls[Anum_pg_largeobject_metadata_lomacl - 1] = true;
|
|
!
|
|
! ntup = heap_form_tuple(RelationGetDescr(pg_lo_meta),
|
|
! values, nulls);
|
|
! if (OidIsValid(loid))
|
|
! HeapTupleSetOid(ntup, loid);
|
|
|
|
! loid_new = simple_heap_insert(pg_lo_meta, ntup);
|
|
! Assert(!OidIsValid(loid) || loid == loid_new);
|
|
|
|
! CatalogUpdateIndexes(pg_lo_meta, ntup);
|
|
|
|
! heap_freetuple(ntup);
|
|
|
|
! heap_close(pg_lo_meta, RowExclusiveLock);
|
|
|
|
! return loid_new;
|
|
}
|
|
|
|
+ /*
|
|
+ * Drop a large object having the given LO identifier.
|
|
+ *
|
|
+ * When we drop a large object, it is necessary to drop both of metadata
|
|
+ * and data pages in same time.
|
|
+ */
|
|
void
|
|
LargeObjectDrop(Oid loid)
|
|
{
|
|
! Relation pg_lo_meta;
|
|
Relation pg_largeobject;
|
|
ScanKeyData skey[1];
|
|
! SysScanDesc scan;
|
|
HeapTuple tuple;
|
|
|
|
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
|
+ RowExclusiveLock);
|
|
+
|
|
+ pg_largeobject = heap_open(LargeObjectRelationId,
|
|
+ RowExclusiveLock);
|
|
+
|
|
+ /*
|
|
+ * Delete an entry from pg_largeobject_metadata
|
|
+ */
|
|
ScanKeyInit(&skey[0],
|
|
! ObjectIdAttributeNumber,
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
! ObjectIdGetDatum(loid));
|
|
|
|
! scan = systable_beginscan(pg_lo_meta,
|
|
! LargeObjectMetadataOidIndexId, true,
|
|
! SnapshotNow, 1, skey);
|
|
|
|
! tuple = systable_getnext(scan);
|
|
! if (!HeapTupleIsValid(tuple))
|
|
! ereport(ERROR,
|
|
! (errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
! errmsg("large object %u does not exist", loid)));
|
|
!
|
|
! simple_heap_delete(pg_lo_meta, &tuple->t_self);
|
|
!
|
|
! systable_endscan(scan);
|
|
!
|
|
! /*
|
|
! * Delete all the associated entries from pg_largeobject
|
|
! */
|
|
! ScanKeyInit(&skey[0],
|
|
! Anum_pg_largeobject_loid,
|
|
! BTEqualStrategyNumber, F_OIDEQ,
|
|
! ObjectIdGetDatum(loid));
|
|
|
|
! scan = systable_beginscan(pg_largeobject,
|
|
! LargeObjectLOidPNIndexId, true,
|
|
! SnapshotNow, 1, skey);
|
|
! while (HeapTupleIsValid(tuple = systable_getnext(scan)))
|
|
{
|
|
simple_heap_delete(pg_largeobject, &tuple->t_self);
|
|
}
|
|
|
|
! systable_endscan(scan);
|
|
|
|
heap_close(pg_largeobject, RowExclusiveLock);
|
|
|
|
! heap_close(pg_lo_meta, RowExclusiveLock);
|
|
! }
|
|
!
|
|
! /*
|
|
! * LargeObjectAlterOwner
|
|
! *
|
|
! * Implementation of ALTER LARGE OBJECT statement
|
|
! */
|
|
! void
|
|
! LargeObjectAlterOwner(Oid loid, Oid newOwnerId)
|
|
! {
|
|
! Form_pg_largeobject_metadata form_lo_meta;
|
|
! Relation pg_lo_meta;
|
|
! ScanKeyData skey[1];
|
|
! SysScanDesc scan;
|
|
! HeapTuple oldtup;
|
|
! HeapTuple newtup;
|
|
!
|
|
! pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
|
! RowExclusiveLock);
|
|
!
|
|
! ScanKeyInit(&skey[0],
|
|
! ObjectIdAttributeNumber,
|
|
! BTEqualStrategyNumber, F_OIDEQ,
|
|
! ObjectIdGetDatum(loid));
|
|
!
|
|
! scan = systable_beginscan(pg_lo_meta,
|
|
! LargeObjectMetadataOidIndexId, true,
|
|
! SnapshotNow, 1, skey);
|
|
!
|
|
! oldtup = systable_getnext(scan);
|
|
! if (!HeapTupleIsValid(oldtup))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("large object %u does not exist", loid)));
|
|
+
|
|
+ form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(oldtup);
|
|
+ if (form_lo_meta->lomowner != newOwnerId)
|
|
+ {
|
|
+ Datum values[Natts_pg_largeobject_metadata];
|
|
+ bool nulls[Natts_pg_largeobject_metadata];
|
|
+ bool replaces[Natts_pg_largeobject_metadata];
|
|
+ Acl *newAcl;
|
|
+ Datum aclDatum;
|
|
+ bool isnull;
|
|
+
|
|
+ /* Superusers can always do it */
|
|
+ if (!superuser())
|
|
+ {
|
|
+ /*
|
|
+ * The 'lo_compat_privileges' is not checked here, because we
|
|
+ * don't have any access control features in the 8.4.x series
|
|
+ * or earlier release.
|
|
+ * So, it is not a place we can define a compatible behavior.
|
|
+ */
|
|
+
|
|
+ /* Otherwise, must be owner of the existing object */
|
|
+ if (!pg_largeobject_ownercheck(loid, GetUserId()))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
+ errmsg("must be owner of large object %u", loid)));
|
|
+
|
|
+ /* Must be able to become new owner */
|
|
+ check_is_member_of_role(GetUserId(), newOwnerId);
|
|
+ }
|
|
+
|
|
+ memset(values, 0, sizeof(values));
|
|
+ memset(nulls, false, sizeof(nulls));
|
|
+ memset(replaces, false, sizeof(nulls));
|
|
+
|
|
+ values[Anum_pg_largeobject_metadata_lomowner - 1]
|
|
+ = ObjectIdGetDatum(newOwnerId);
|
|
+ replaces[Anum_pg_largeobject_metadata_lomowner - 1] = true;
|
|
+
|
|
+ /*
|
|
+ * Determine the modified ACL for the new owner.
|
|
+ * This is only necessary when the ACL is non-null.
|
|
+ */
|
|
+ aclDatum = heap_getattr(oldtup,
|
|
+ Anum_pg_largeobject_metadata_lomacl,
|
|
+ RelationGetDescr(pg_lo_meta), &isnull);
|
|
+ if (!isnull)
|
|
+ {
|
|
+ newAcl = aclnewowner(DatumGetAclP(aclDatum),
|
|
+ form_lo_meta->lomowner, newOwnerId);
|
|
+ values[Anum_pg_largeobject_metadata_lomacl - 1]
|
|
+ = PointerGetDatum(newAcl);
|
|
+ replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
|
|
+ }
|
|
+
|
|
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_lo_meta),
|
|
+ values, nulls, replaces);
|
|
+
|
|
+ simple_heap_update(pg_lo_meta, &newtup->t_self, newtup);
|
|
+ CatalogUpdateIndexes(pg_lo_meta, newtup);
|
|
+
|
|
+ heap_freetuple(newtup);
|
|
+
|
|
+ /* Update owner dependency reference */
|
|
+ changeDependencyOnOwner(LargeObjectRelationId,
|
|
+ loid, newOwnerId);
|
|
+ }
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(pg_lo_meta, RowExclusiveLock);
|
|
}
|
|
|
|
+ /*
|
|
+ * LargeObjectExists
|
|
+ *
|
|
+ * Currently, we don't use system cache to contain metadata of
|
|
+ * large objects, because massive number of large objects can
|
|
+ * consume not a small amount of process local memory.
|
|
+ *
|
|
+ * Note that LargeObjectExists always scans the system catalog
|
|
+ * with SnapshotNow, so it is unavailable to use to check
|
|
+ * existence in read-only accesses.
|
|
+ */
|
|
bool
|
|
LargeObjectExists(Oid loid)
|
|
{
|
|
+ Relation pg_lo_meta;
|
|
+ ScanKeyData skey[1];
|
|
+ SysScanDesc sd;
|
|
+ HeapTuple tuple;
|
|
bool retval = false;
|
|
|
|
ScanKeyInit(&skey[0],
|
|
! ObjectIdAttributeNumber,
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
ObjectIdGetDatum(loid));
|
|
|
|
! pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
|
! AccessShareLock);
|
|
|
|
! sd = systable_beginscan(pg_lo_meta,
|
|
! LargeObjectMetadataOidIndexId, true,
|
|
SnapshotNow, 1, skey);
|
|
|
|
! tuple = systable_getnext(sd);
|
|
! if (HeapTupleIsValid(tuple))
|
|
retval = true;
|
|
|
|
systable_endscan(sd);
|
|
|
|
! heap_close(pg_lo_meta, AccessShareLock);
|
|
|
|
return retval;
|
|
}
|
|
diff -Nrpc base/src/backend/catalog/pg_shdepend.c blob/src/backend/catalog/pg_shdepend.c
|
|
*** base/src/backend/catalog/pg_shdepend.c Thu Jun 18 10:20:52 2009
|
|
--- blob/src/backend/catalog/pg_shdepend.c Fri Dec 18 09:40:55 2009
|
|
***************
|
|
*** 24,29 ****
|
|
--- 24,30 ----
|
|
#include "catalog/pg_conversion.h"
|
|
#include "catalog/pg_database.h"
|
|
#include "catalog/pg_language.h"
|
|
+ #include "catalog/pg_largeobject.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_operator.h"
|
|
#include "catalog/pg_proc.h"
|
|
*************** shdepDropOwned(List *roleids, DropBehavi
|
|
*** 1210,1215 ****
|
|
--- 1211,1219 ----
|
|
case LanguageRelationId:
|
|
istmt.objtype = ACL_OBJECT_LANGUAGE;
|
|
break;
|
|
+ case LargeObjectRelationId:
|
|
+ istmt.objtype = ACL_OBJECT_LARGEOBJECT;
|
|
+ break;
|
|
case NamespaceRelationId:
|
|
istmt.objtype = ACL_OBJECT_NAMESPACE;
|
|
break;
|
|
*************** shdepReassignOwned(List *roleids, Oid ne
|
|
*** 1365,1370 ****
|
|
--- 1369,1378 ----
|
|
AlterLanguageOwner_oid(sdepForm->objid, newrole);
|
|
break;
|
|
|
|
+ case LargeObjectRelationId:
|
|
+ LargeObjectAlterOwner(sdepForm->objid, newrole);
|
|
+ break;
|
|
+
|
|
default:
|
|
elog(ERROR, "unexpected classid %d", sdepForm->classid);
|
|
break;
|
|
diff -Nrpc base/src/backend/commands/alter.c blob/src/backend/commands/alter.c
|
|
*** base/src/backend/commands/alter.c Sat Jan 3 13:01:35 2009
|
|
--- blob/src/backend/commands/alter.c Fri Dec 18 09:40:55 2009
|
|
***************
|
|
*** 15,20 ****
|
|
--- 15,21 ----
|
|
#include "postgres.h"
|
|
|
|
#include "catalog/namespace.h"
|
|
+ #include "catalog/pg_largeobject.h"
|
|
#include "commands/alter.h"
|
|
#include "commands/conversioncmds.h"
|
|
#include "commands/dbcommands.h"
|
|
*************** ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
|
|
*** 233,238 ****
|
|
--- 234,243 ----
|
|
AlterLanguageOwner(strVal(linitial(stmt->object)), newowner);
|
|
break;
|
|
|
|
+ case OBJECT_LARGEOBJECT:
|
|
+ LargeObjectAlterOwner(intVal(linitial(stmt->object)), newowner);
|
|
+ break;
|
|
+
|
|
case OBJECT_OPERATOR:
|
|
Assert(list_length(stmt->objarg) == 2);
|
|
AlterOperatorOwner(stmt->object,
|
|
diff -Nrpc base/src/backend/commands/comment.c blob/src/backend/commands/comment.c
|
|
*** base/src/backend/commands/comment.c Thu Jun 18 10:20:52 2009
|
|
--- blob/src/backend/commands/comment.c Fri Dec 18 09:40:55 2009
|
|
***************
|
|
*** 25,30 ****
|
|
--- 25,31 ----
|
|
#include "catalog/pg_description.h"
|
|
#include "catalog/pg_language.h"
|
|
#include "catalog/pg_largeobject.h"
|
|
+ #include "catalog/pg_largeobject_metadata.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_opclass.h"
|
|
#include "catalog/pg_operator.h"
|
|
***************
|
|
*** 42,47 ****
|
|
--- 43,49 ----
|
|
#include "commands/comment.h"
|
|
#include "commands/dbcommands.h"
|
|
#include "commands/tablespace.h"
|
|
+ #include "libpq/be-fsstubs.h"
|
|
#include "miscadmin.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "parser/parse_func.h"
|
|
*************** CommentLargeObject(List *qualname, char
|
|
*** 1422,1428 ****
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("large object %u does not exist", loid)));
|
|
|
|
! /* Call CreateComments() to create/drop the comments */
|
|
CreateComments(loid, LargeObjectRelationId, 0, comment);
|
|
}
|
|
|
|
--- 1424,1443 ----
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("large object %u does not exist", loid)));
|
|
|
|
! /* Permission checks */
|
|
! if (!lo_compat_privileges &&
|
|
! !pg_largeobject_ownercheck(loid, GetUserId()))
|
|
! ereport(ERROR,
|
|
! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
! errmsg("must be owner of large object %u", loid)));
|
|
!
|
|
! /*
|
|
! * Call CreateComments() to create/drop the comments
|
|
! *
|
|
! * See the comment in the inv_create() which describes
|
|
! * the reason why LargeObjectRelationId is used instead
|
|
! * of the LargeObjectMetadataRelationId.
|
|
! */
|
|
CreateComments(loid, LargeObjectRelationId, 0, comment);
|
|
}
|
|
|
|
diff -Nrpc base/src/backend/commands/tablecmds.c blob/src/backend/commands/tablecmds.c
|
|
*** base/src/backend/commands/tablecmds.c Tue Dec 15 17:16:51 2009
|
|
--- blob/src/backend/commands/tablecmds.c Fri Dec 18 09:40:55 2009
|
|
*************** ATExecAlterColumnType(AlteredTableInfo *
|
|
*** 5902,5907 ****
|
|
--- 5902,5908 ----
|
|
case OCLASS_CAST:
|
|
case OCLASS_CONVERSION:
|
|
case OCLASS_LANGUAGE:
|
|
+ case OCLASS_LARGEOBJECT:
|
|
case OCLASS_OPERATOR:
|
|
case OCLASS_OPCLASS:
|
|
case OCLASS_OPFAMILY:
|
|
diff -Nrpc base/src/backend/libpq/be-fsstubs.c blob/src/backend/libpq/be-fsstubs.c
|
|
*** base/src/backend/libpq/be-fsstubs.c Thu Jun 18 10:20:52 2009
|
|
--- blob/src/backend/libpq/be-fsstubs.c Fri Dec 18 09:40:55 2009
|
|
***************
|
|
*** 42,55 ****
|
|
--- 42,61 ----
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
+ #include "catalog/pg_largeobject_metadata.h"
|
|
#include "libpq/be-fsstubs.h"
|
|
#include "libpq/libpq-fs.h"
|
|
#include "miscadmin.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/large_object.h"
|
|
+ #include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/memutils.h"
|
|
|
|
+ /*
|
|
+ * compatibility flag for permission checks
|
|
+ */
|
|
+ bool lo_compat_privileges;
|
|
|
|
/*#define FSDB 1*/
|
|
#define BUFSIZE 8192
|
|
*************** lo_read(int fd, char *buf, int len)
|
|
*** 156,161 ****
|
|
--- 162,178 ----
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("invalid large-object descriptor: %d", fd)));
|
|
|
|
+ /* Permission checks */
|
|
+ if (!lo_compat_privileges &&
|
|
+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
|
|
+ GetUserId(),
|
|
+ ACL_SELECT,
|
|
+ cookies[fd]->snapshot) != ACLCHECK_OK)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
+ errmsg("permission denied for large object %u",
|
|
+ cookies[fd]->id)));
|
|
+
|
|
status = inv_read(cookies[fd], buf, len);
|
|
|
|
return status;
|
|
*************** lo_write(int fd, const char *buf, int le
|
|
*** 177,182 ****
|
|
--- 194,210 ----
|
|
errmsg("large object descriptor %d was not opened for writing",
|
|
fd)));
|
|
|
|
+ /* Permission checks */
|
|
+ if (!lo_compat_privileges &&
|
|
+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
|
|
+ GetUserId(),
|
|
+ ACL_UPDATE,
|
|
+ cookies[fd]->snapshot) != ACLCHECK_OK)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
+ errmsg("permission denied for large object %u",
|
|
+ cookies[fd]->id)));
|
|
+
|
|
status = inv_write(cookies[fd], buf, len);
|
|
|
|
return status;
|
|
*************** lo_unlink(PG_FUNCTION_ARGS)
|
|
*** 251,256 ****
|
|
--- 279,291 ----
|
|
{
|
|
Oid lobjId = PG_GETARG_OID(0);
|
|
|
|
+ /* Must be owner of the largeobject */
|
|
+ if (!lo_compat_privileges &&
|
|
+ !pg_largeobject_ownercheck(lobjId, GetUserId()))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
+ errmsg("must be owner of large object %u", lobjId)));
|
|
+
|
|
/*
|
|
* If there are any open LO FDs referencing that ID, close 'em.
|
|
*/
|
|
*************** lo_truncate(PG_FUNCTION_ARGS)
|
|
*** 482,487 ****
|
|
--- 517,533 ----
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("invalid large-object descriptor: %d", fd)));
|
|
|
|
+ /* Permission checks */
|
|
+ if (!lo_compat_privileges &&
|
|
+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
|
|
+ GetUserId(),
|
|
+ ACL_UPDATE,
|
|
+ cookies[fd]->snapshot) != ACLCHECK_OK)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
+ errmsg("permission denied for large object %u",
|
|
+ cookies[fd]->id)));
|
|
+
|
|
inv_truncate(cookies[fd], len);
|
|
|
|
PG_RETURN_INT32(0);
|
|
diff -Nrpc base/src/backend/parser/gram.y blob/src/backend/parser/gram.y
|
|
*** base/src/backend/parser/gram.y Sun Sep 6 19:40:49 2009
|
|
--- blob/src/backend/parser/gram.y Fri Dec 18 09:40:55 2009
|
|
*************** static TypeName *TableFuncTypeName(List
|
|
*** 378,383 ****
|
|
--- 378,384 ----
|
|
%type <boolean> opt_varying opt_timezone
|
|
|
|
%type <ival> Iconst SignedIconst
|
|
+ %type <list> Iconst_list
|
|
%type <str> Sconst comment_text
|
|
%type <str> RoleId opt_granted_by opt_boolean ColId_or_Sconst
|
|
%type <list> var_list
|
|
*************** privilege_target:
|
|
*** 4379,4384 ****
|
|
--- 4380,4392 ----
|
|
n->objs = $2;
|
|
$$ = n;
|
|
}
|
|
+ | LARGE_P OBJECT_P Iconst_list
|
|
+ {
|
|
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
|
+ n->objtype = ACL_OBJECT_LARGEOBJECT;
|
|
+ n->objs = $3;
|
|
+ $$ = n;
|
|
+ }
|
|
| SCHEMA name_list
|
|
{
|
|
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
|
|
*************** AlterOwnerStmt: ALTER AGGREGATE func_nam
|
|
*** 5506,5511 ****
|
|
--- 5514,5527 ----
|
|
n->newowner = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
+ | ALTER LARGE_P OBJECT_P Iconst OWNER TO RoleId
|
|
+ {
|
|
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
+ n->objectType = OBJECT_LARGEOBJECT;
|
|
+ n->object = list_make1(makeInteger($4));
|
|
+ n->newowner = $7;
|
|
+ $$ = (Node *)n;
|
|
+ }
|
|
| ALTER OPERATOR any_operator oper_argtypes OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
*************** SignedIconst: Iconst { $$ = $1; }
|
|
*** 10066,10071 ****
|
|
--- 10082,10091 ----
|
|
| '-' Iconst { $$ = - $2; }
|
|
;
|
|
|
|
+ Iconst_list: Iconst { $$ = list_make1(makeInteger($1)); }
|
|
+ | Iconst_list ',' Iconst { $$ = lappend($1, makeInteger($3)); }
|
|
+ ;
|
|
+
|
|
/*
|
|
* Name classification hierarchy.
|
|
*
|
|
diff -Nrpc base/src/backend/storage/large_object/inv_api.c blob/src/backend/storage/large_object/inv_api.c
|
|
*** base/src/backend/storage/large_object/inv_api.c Thu Jun 18 10:20:52 2009
|
|
--- blob/src/backend/storage/large_object/inv_api.c Fri Dec 18 09:40:55 2009
|
|
***************
|
|
*** 32,49 ****
|
|
--- 32,54 ----
|
|
|
|
#include "access/genam.h"
|
|
#include "access/heapam.h"
|
|
+ #include "access/sysattr.h"
|
|
#include "access/tuptoaster.h"
|
|
#include "access/xact.h"
|
|
#include "catalog/catalog.h"
|
|
+ #include "catalog/dependency.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/pg_largeobject.h"
|
|
+ #include "catalog/pg_largeobject_metadata.h"
|
|
#include "commands/comment.h"
|
|
#include "libpq/libpq-fs.h"
|
|
+ #include "miscadmin.h"
|
|
#include "storage/large_object.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/resowner.h"
|
|
#include "utils/snapmgr.h"
|
|
+ #include "utils/syscache.h"
|
|
#include "utils/tqual.h"
|
|
|
|
|
|
*************** close_lo_relation(bool isCommit)
|
|
*** 139,168 ****
|
|
static bool
|
|
myLargeObjectExists(Oid loid, Snapshot snapshot)
|
|
{
|
|
bool retval = false;
|
|
- Relation pg_largeobject;
|
|
- ScanKeyData skey[1];
|
|
- SysScanDesc sd;
|
|
|
|
- /*
|
|
- * See if we can find any tuples belonging to the specified LO
|
|
- */
|
|
ScanKeyInit(&skey[0],
|
|
! Anum_pg_largeobject_loid,
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
ObjectIdGetDatum(loid));
|
|
|
|
! pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock);
|
|
|
|
! sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
|
|
snapshot, 1, skey);
|
|
|
|
! if (systable_getnext(sd) != NULL)
|
|
retval = true;
|
|
|
|
systable_endscan(sd);
|
|
|
|
! heap_close(pg_largeobject, AccessShareLock);
|
|
|
|
return retval;
|
|
}
|
|
--- 144,174 ----
|
|
static bool
|
|
myLargeObjectExists(Oid loid, Snapshot snapshot)
|
|
{
|
|
+ Relation pg_lo_meta;
|
|
+ ScanKeyData skey[1];
|
|
+ SysScanDesc sd;
|
|
+ HeapTuple tuple;
|
|
bool retval = false;
|
|
|
|
ScanKeyInit(&skey[0],
|
|
! ObjectIdAttributeNumber,
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
ObjectIdGetDatum(loid));
|
|
|
|
! pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
|
! AccessShareLock);
|
|
|
|
! sd = systable_beginscan(pg_lo_meta,
|
|
! LargeObjectMetadataOidIndexId, true,
|
|
snapshot, 1, skey);
|
|
|
|
! tuple = systable_getnext(sd);
|
|
! if (HeapTupleIsValid(tuple))
|
|
retval = true;
|
|
|
|
systable_endscan(sd);
|
|
|
|
! heap_close(pg_lo_meta, AccessShareLock);
|
|
|
|
return retval;
|
|
}
|
|
*************** getbytealen(bytea *data)
|
|
*** 193,223 ****
|
|
Oid
|
|
inv_create(Oid lobjId)
|
|
{
|
|
/*
|
|
! * Allocate an OID to be the LO's identifier, unless we were told what to
|
|
! * use. We can use the index on pg_largeobject for checking OID
|
|
! * uniqueness, even though it has additional columns besides OID.
|
|
*/
|
|
! if (!OidIsValid(lobjId))
|
|
! {
|
|
! open_lo_relation();
|
|
!
|
|
! lobjId = GetNewOidWithIndex(lo_heap_r, LargeObjectLOidPNIndexId,
|
|
! Anum_pg_largeobject_loid);
|
|
! }
|
|
|
|
/*
|
|
! * Create the LO by writing an empty first page for it in pg_largeobject
|
|
! * (will fail if duplicate)
|
|
*/
|
|
! LargeObjectCreate(lobjId);
|
|
!
|
|
/*
|
|
* Advance command counter to make new tuple visible to later operations.
|
|
*/
|
|
CommandCounterIncrement();
|
|
|
|
! return lobjId;
|
|
}
|
|
|
|
/*
|
|
--- 199,229 ----
|
|
Oid
|
|
inv_create(Oid lobjId)
|
|
{
|
|
+ Oid lobjId_new;
|
|
+
|
|
/*
|
|
! * Create a new largeobject with empty data pages
|
|
*/
|
|
! lobjId_new = LargeObjectCreate(lobjId);
|
|
|
|
/*
|
|
! * dependency on the owner of largeobject
|
|
! *
|
|
! * The reason why we use LargeObjectRelationId instead of
|
|
! * LargeObjectMetadataRelationId here is to provide backward
|
|
! * compatibility to the applications which utilize a knowledge
|
|
! * about internal layout of system catalogs.
|
|
! * OID of pg_largeobject_metadata and loid of pg_largeobject
|
|
! * are same value, so there are no actual differences here.
|
|
*/
|
|
! recordDependencyOnOwner(LargeObjectRelationId,
|
|
! lobjId_new, GetUserId());
|
|
/*
|
|
* Advance command counter to make new tuple visible to later operations.
|
|
*/
|
|
CommandCounterIncrement();
|
|
|
|
! return lobjId_new;
|
|
}
|
|
|
|
/*
|
|
*************** inv_close(LargeObjectDesc *obj_desc)
|
|
*** 292,301 ****
|
|
int
|
|
inv_drop(Oid lobjId)
|
|
{
|
|
! LargeObjectDrop(lobjId);
|
|
|
|
! /* Delete any comments on the large object */
|
|
! DeleteComments(lobjId, LargeObjectRelationId, 0);
|
|
|
|
/*
|
|
* Advance command counter so that tuple removal will be seen by later
|
|
--- 298,312 ----
|
|
int
|
|
inv_drop(Oid lobjId)
|
|
{
|
|
! ObjectAddress object;
|
|
|
|
! /*
|
|
! * Delete any comments and dependencies on the large object
|
|
! */
|
|
! object.classId = LargeObjectRelationId;
|
|
! object.objectId = lobjId;
|
|
! object.objectSubId = 0;
|
|
! performDeletion(&object, DROP_CASCADE);
|
|
|
|
/*
|
|
* Advance command counter so that tuple removal will be seen by later
|
|
*************** inv_drop(Oid lobjId)
|
|
*** 315,321 ****
|
|
static uint32
|
|
inv_getsize(LargeObjectDesc *obj_desc)
|
|
{
|
|
- bool found = false;
|
|
uint32 lastbyte = 0;
|
|
ScanKeyData skey[1];
|
|
SysScanDesc sd;
|
|
--- 326,331 ----
|
|
*************** inv_getsize(LargeObjectDesc *obj_desc)
|
|
*** 339,351 ****
|
|
* large object in reverse pageno order. So, it's sufficient to examine
|
|
* the first valid tuple (== last valid page).
|
|
*/
|
|
! while ((tuple = systable_getnext_ordered(sd, BackwardScanDirection)) != NULL)
|
|
{
|
|
Form_pg_largeobject data;
|
|
bytea *datafield;
|
|
bool pfreeit;
|
|
|
|
- found = true;
|
|
if (HeapTupleHasNulls(tuple)) /* paranoia */
|
|
elog(ERROR, "null field found in pg_largeobject");
|
|
data = (Form_pg_largeobject) GETSTRUCT(tuple);
|
|
--- 349,361 ----
|
|
* large object in reverse pageno order. So, it's sufficient to examine
|
|
* the first valid tuple (== last valid page).
|
|
*/
|
|
! tuple = systable_getnext_ordered(sd, BackwardScanDirection);
|
|
! if (HeapTupleIsValid(tuple))
|
|
{
|
|
Form_pg_largeobject data;
|
|
bytea *datafield;
|
|
bool pfreeit;
|
|
|
|
if (HeapTupleHasNulls(tuple)) /* paranoia */
|
|
elog(ERROR, "null field found in pg_largeobject");
|
|
data = (Form_pg_largeobject) GETSTRUCT(tuple);
|
|
*************** inv_getsize(LargeObjectDesc *obj_desc)
|
|
*** 360,374 ****
|
|
lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
|
|
if (pfreeit)
|
|
pfree(datafield);
|
|
- break;
|
|
}
|
|
|
|
systable_endscan_ordered(sd);
|
|
|
|
- if (!found)
|
|
- ereport(ERROR,
|
|
- (errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
- errmsg("large object %u does not exist", obj_desc->id)));
|
|
return lastbyte;
|
|
}
|
|
|
|
--- 370,379 ----
|
|
*************** inv_write(LargeObjectDesc *obj_desc, con
|
|
*** 545,550 ****
|
|
--- 550,561 ----
|
|
errmsg("large object %u was not opened for writing",
|
|
obj_desc->id)));
|
|
|
|
+ /* check existence of the target largeobject */
|
|
+ if (!LargeObjectExists(obj_desc->id))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
+ errmsg("large object %u was already dropped", obj_desc->id)));
|
|
+
|
|
if (nbytes <= 0)
|
|
return 0;
|
|
|
|
*************** inv_truncate(LargeObjectDesc *obj_desc,
|
|
*** 736,741 ****
|
|
--- 747,758 ----
|
|
errmsg("large object %u was not opened for writing",
|
|
obj_desc->id)));
|
|
|
|
+ /* check existence of the target largeobject */
|
|
+ if (!LargeObjectExists(obj_desc->id))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
+ errmsg("large object %u was already dropped", obj_desc->id)));
|
|
+
|
|
open_lo_relation();
|
|
|
|
indstate = CatalogOpenIndexes(lo_heap_r);
|
|
diff -Nrpc base/src/backend/tcop/utility.c blob/src/backend/tcop/utility.c
|
|
*** base/src/backend/tcop/utility.c Tue Dec 15 17:16:51 2009
|
|
--- blob/src/backend/tcop/utility.c Fri Dec 18 09:40:55 2009
|
|
*************** CreateCommandTag(Node *parsetree)
|
|
*** 1625,1630 ****
|
|
--- 1625,1633 ----
|
|
case OBJECT_LANGUAGE:
|
|
tag = "ALTER LANGUAGE";
|
|
break;
|
|
+ case OBJECT_LARGEOBJECT:
|
|
+ tag = "ALTER LARGEOBJECT";
|
|
+ break;
|
|
case OBJECT_OPERATOR:
|
|
tag = "ALTER OPERATOR";
|
|
break;
|
|
diff -Nrpc base/src/backend/utils/adt/acl.c blob/src/backend/utils/adt/acl.c
|
|
*** base/src/backend/utils/adt/acl.c Thu Jun 18 10:20:52 2009
|
|
--- blob/src/backend/utils/adt/acl.c Fri Dec 18 09:40:55 2009
|
|
*************** acldefault(GrantObjectType objtype, Oid
|
|
*** 631,636 ****
|
|
--- 631,641 ----
|
|
world_default = ACL_USAGE;
|
|
owner_default = ACL_ALL_RIGHTS_LANGUAGE;
|
|
break;
|
|
+ case ACL_OBJECT_LARGEOBJECT:
|
|
+ /* Grant SELECT,UPDATE by default, for now */
|
|
+ world_default = ACL_NO_RIGHTS;
|
|
+ owner_default = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
+ break;
|
|
case ACL_OBJECT_NAMESPACE:
|
|
world_default = ACL_NO_RIGHTS;
|
|
owner_default = ACL_ALL_RIGHTS_NAMESPACE;
|
|
diff -Nrpc base/src/backend/utils/misc/guc.c blob/src/backend/utils/misc/guc.c
|
|
*** base/src/backend/utils/misc/guc.c Thu Mar 18 01:40:54 2010
|
|
--- blob/src/backend/utils/misc/guc.c Thu Mar 18 09:43:03 2010
|
|
***************
|
|
*** 38,43 ****
|
|
--- 38,44 ----
|
|
#include "commands/trigger.h"
|
|
#include "funcapi.h"
|
|
#include "libpq/auth.h"
|
|
+ #include "libpq/be-fsstubs.h"
|
|
#include "libpq/pqformat.h"
|
|
#include "miscadmin.h"
|
|
#include "optimizer/cost.h"
|
|
*************** static struct config_bool ConfigureNames
|
|
*** 1222,1227 ****
|
|
--- 1223,1238 ----
|
|
false, NULL, NULL
|
|
},
|
|
|
|
+ {
|
|
+ {"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
|
|
+ gettext_noop("Enables backward compatibility in privilege checks on large objects"),
|
|
+ gettext_noop("When turned on, privilege checks on large objects perform "
|
|
+ "with backward compatibility as 8.4.x or earlier releases.")
|
|
+ },
|
|
+ &lo_compat_privileges,
|
|
+ false, NULL, NULL
|
|
+ },
|
|
+
|
|
/* End-of-list marker */
|
|
{
|
|
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL
|
|
diff -Nrpc base/src/backend/utils/misc/postgresql.conf.sample blob/src/backend/utils/misc/postgresql.conf.sample
|
|
*** base/src/backend/utils/misc/postgresql.conf.sample Thu Mar 18 01:40:54 2010
|
|
--- blob/src/backend/utils/misc/postgresql.conf.sample Thu Mar 18 09:43:03 2010
|
|
***************
|
|
*** 484,489 ****
|
|
--- 484,490 ----
|
|
#backslash_quote = safe_encoding # on, off, or safe_encoding
|
|
#default_with_oids = off
|
|
#escape_string_warning = on
|
|
+ #lo_compat_privileges = off
|
|
#regex_flavor = advanced # advanced, extended, or basic
|
|
#sql_inheritance = on
|
|
#standard_conforming_strings = off
|
|
diff -Nrpc base/src/bin/initdb/initdb.c blob/src/bin/initdb/initdb.c
|
|
*** base/src/bin/initdb/initdb.c Tue Dec 15 17:16:51 2009
|
|
--- blob/src/bin/initdb/initdb.c Fri Dec 18 09:40:55 2009
|
|
*************** setup_privileges(void)
|
|
*** 1815,1820 ****
|
|
--- 1815,1821 ----
|
|
" WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n",
|
|
"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",
|
|
NULL
|
|
};
|
|
|
|
diff -Nrpc base/src/bin/pg_dump/dumputils.c blob/src/bin/pg_dump/dumputils.c
|
|
*** base/src/bin/pg_dump/dumputils.c Thu Mar 18 01:40:54 2010
|
|
--- blob/src/bin/pg_dump/dumputils.c Thu Mar 18 09:43:03 2010
|
|
*************** do { \
|
|
*** 758,763 ****
|
|
--- 758,768 ----
|
|
CONVERT_PRIV('U', "USAGE");
|
|
else if (strcmp(type, "FOREIGN SERVER") == 0)
|
|
CONVERT_PRIV('U', "USAGE");
|
|
+ else if (strcmp(type, "LARGE OBJECT") == 0)
|
|
+ {
|
|
+ CONVERT_PRIV('r', "SELECT");
|
|
+ CONVERT_PRIV('w', "UPDATE");
|
|
+ }
|
|
else
|
|
abort();
|
|
|
|
diff -Nrpc base/src/bin/pg_dump/pg_dump.c blob/src/bin/pg_dump/pg_dump.c
|
|
*** base/src/bin/pg_dump/pg_dump.c Thu Mar 18 01:40:54 2010
|
|
--- blob/src/bin/pg_dump/pg_dump.c Thu Mar 18 09:43:03 2010
|
|
*************** hasBlobs(Archive *AH)
|
|
*** 1923,1929 ****
|
|
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";
|
|
--- 1923,1931 ----
|
|
selectSourceSchema("pg_catalog");
|
|
|
|
/* Check for BLOB OIDs */
|
|
! if (AH->remoteVersion >= 80402)
|
|
! 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)
|
|
*** 1959,1965 ****
|
|
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'";
|
|
--- 1961,1969 ----
|
|
selectSourceSchema("pg_catalog");
|
|
|
|
/* Cursor to get all BLOB OIDs */
|
|
! if (AH->remoteVersion >= 80402)
|
|
! 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'";
|
|
*************** dumpBlobs(Archive *AH, void *arg)
|
|
*** 2023,2029 ****
|
|
|
|
/*
|
|
* dumpBlobComments
|
|
! * dump all blob comments
|
|
*
|
|
* Since we don't provide any way to be selective about dumping blobs,
|
|
* there's no need to be selective about their comments either. We put
|
|
--- 2027,2035 ----
|
|
|
|
/*
|
|
* dumpBlobComments
|
|
! * dump all blob properties.
|
|
! * It has "BLOB COMMENTS" tag due to the historical reason, but note
|
|
! * that it is the routine to dump all the properties of blobs.
|
|
*
|
|
* Since we don't provide any way to be selective about dumping blobs,
|
|
* there's no need to be selective about their comments either. We put
|
|
*************** dumpBlobComments(Archive *AH, void *arg)
|
|
*** 2034,2063 ****
|
|
{
|
|
const char *blobQry;
|
|
const char *blobFetchQry;
|
|
! PQExpBuffer commentcmd = createPQExpBuffer();
|
|
PGresult *res;
|
|
int i;
|
|
|
|
if (g_verbose)
|
|
! write_msg(NULL, "saving large object comments\n");
|
|
|
|
/* Make sure we are in proper schema */
|
|
selectSourceSchema("pg_catalog");
|
|
|
|
/* Cursor to get all BLOB comments */
|
|
! if (AH->remoteVersion >= 70300)
|
|
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
|
|
! "obj_description(loid, 'pg_largeobject') "
|
|
"FROM (SELECT DISTINCT loid FROM "
|
|
"pg_description d JOIN pg_largeobject l ON (objoid = loid) "
|
|
"WHERE classoid = 'pg_largeobject'::regclass) ss";
|
|
else if (AH->remoteVersion >= 70200)
|
|
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
|
|
! "obj_description(loid, 'pg_largeobject') "
|
|
"FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
|
|
else if (AH->remoteVersion >= 70100)
|
|
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
|
|
! "obj_description(loid) "
|
|
"FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
|
|
else
|
|
blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, "
|
|
--- 2040,2074 ----
|
|
{
|
|
const char *blobQry;
|
|
const char *blobFetchQry;
|
|
! PQExpBuffer cmdQry = createPQExpBuffer();
|
|
PGresult *res;
|
|
int i;
|
|
|
|
if (g_verbose)
|
|
! write_msg(NULL, "saving large object properties\n");
|
|
|
|
/* Make sure we are in proper schema */
|
|
selectSourceSchema("pg_catalog");
|
|
|
|
/* Cursor to get all BLOB comments */
|
|
! if (AH->remoteVersion >= 80402)
|
|
! blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, "
|
|
! "obj_description(oid, 'pg_largeobject'), "
|
|
! "pg_get_userbyid(lomowner), lomacl "
|
|
! "FROM pg_largeobject_metadata";
|
|
! else if (AH->remoteVersion >= 70300)
|
|
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
|
|
! "obj_description(loid, 'pg_largeobject'), NULL, NULL "
|
|
"FROM (SELECT DISTINCT loid FROM "
|
|
"pg_description d JOIN pg_largeobject l ON (objoid = loid) "
|
|
"WHERE classoid = 'pg_largeobject'::regclass) ss";
|
|
else if (AH->remoteVersion >= 70200)
|
|
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
|
|
! "obj_description(loid, 'pg_largeobject'), NULL, NULL "
|
|
"FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
|
|
else if (AH->remoteVersion >= 70100)
|
|
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
|
|
! "obj_description(loid), NULL, NULL "
|
|
"FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
|
|
else
|
|
blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, "
|
|
*************** dumpBlobComments(Archive *AH, void *arg)
|
|
*** 2065,2071 ****
|
|
" SELECT description "
|
|
" FROM pg_description pd "
|
|
" WHERE pd.objoid=pc.oid "
|
|
! " ) "
|
|
"FROM pg_class pc WHERE relkind = 'l'";
|
|
|
|
res = PQexec(g_conn, blobQry);
|
|
--- 2076,2082 ----
|
|
" SELECT description "
|
|
" FROM pg_description pd "
|
|
" WHERE pd.objoid=pc.oid "
|
|
! " ), NULL, NULL "
|
|
"FROM pg_class pc WHERE relkind = 'l'";
|
|
|
|
res = PQexec(g_conn, blobQry);
|
|
*************** dumpBlobComments(Archive *AH, void *arg)
|
|
*** 2085,2106 ****
|
|
/* Process the tuples, if any */
|
|
for (i = 0; i < PQntuples(res); i++)
|
|
{
|
|
! Oid blobOid;
|
|
! char *comment;
|
|
|
|
! /* ignore blobs without comments */
|
|
! if (PQgetisnull(res, i, 1))
|
|
! continue;
|
|
|
|
! blobOid = atooid(PQgetvalue(res, i, 0));
|
|
! comment = PQgetvalue(res, i, 1);
|
|
|
|
! printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ",
|
|
! blobOid);
|
|
! appendStringLiteralAH(commentcmd, comment, AH);
|
|
! appendPQExpBuffer(commentcmd, ";\n");
|
|
|
|
! archputs(commentcmd->data, AH);
|
|
}
|
|
} while (PQntuples(res) > 0);
|
|
|
|
--- 2096,2146 ----
|
|
/* Process the tuples, if any */
|
|
for (i = 0; i < PQntuples(res); i++)
|
|
{
|
|
! Oid blobOid = atooid(PQgetvalue(res, i, 0));
|
|
! char *lo_comment = PQgetvalue(res, i, 1);
|
|
! char *lo_owner = PQgetvalue(res, i, 2);
|
|
! char *lo_acl = PQgetvalue(res, i, 3);
|
|
! char lo_name[32];
|
|
|
|
! resetPQExpBuffer(cmdQry);
|
|
|
|
! /* comment on the blob */
|
|
! if (!PQgetisnull(res, i, 1))
|
|
! {
|
|
! appendPQExpBuffer(cmdQry,
|
|
! "COMMENT ON LARGE OBJECT %u IS ", blobOid);
|
|
! appendStringLiteralAH(cmdQry, lo_comment, AH);
|
|
! appendPQExpBuffer(cmdQry, ";\n");
|
|
! }
|
|
!
|
|
! /* dump blob ownership, if necessary */
|
|
! if (!PQgetisnull(res, i, 2))
|
|
! {
|
|
! appendPQExpBuffer(cmdQry,
|
|
! "ALTER LARGE OBJECT %u OWNER TO %s;\n",
|
|
! blobOid, lo_owner);
|
|
! }
|
|
|
|
! /* dump blob privileges, if necessary */
|
|
! if (!PQgetisnull(res, i, 3) &&
|
|
! !dataOnly && !aclsSkip)
|
|
! {
|
|
! snprintf(lo_name, sizeof(lo_name), "%u", blobOid);
|
|
! if (!buildACLCommands(lo_name, NULL, "LARGE OBJECT",
|
|
! lo_acl, lo_owner,
|
|
! AH->remoteVersion, cmdQry))
|
|
! {
|
|
! write_msg(NULL, "could not parse ACL (%s) for "
|
|
! "large object %u", lo_acl, blobOid);
|
|
! exit_nicely();
|
|
! }
|
|
! }
|
|
|
|
! if (cmdQry->len > 0)
|
|
! {
|
|
! appendPQExpBuffer(cmdQry, "\n");
|
|
! archputs(cmdQry->data, AH);
|
|
! }
|
|
}
|
|
} while (PQntuples(res) > 0);
|
|
|
|
*************** dumpBlobComments(Archive *AH, void *arg)
|
|
*** 2108,2114 ****
|
|
|
|
archputs("\n", AH);
|
|
|
|
! destroyPQExpBuffer(commentcmd);
|
|
|
|
return 1;
|
|
}
|
|
--- 2148,2154 ----
|
|
|
|
archputs("\n", AH);
|
|
|
|
! destroyPQExpBuffer(cmdQry);
|
|
|
|
return 1;
|
|
}
|
|
diff -Nrpc base/src/bin/psql/large_obj.c blob/src/bin/psql/large_obj.c
|
|
*** base/src/bin/psql/large_obj.c Sat Jan 3 12:49:23 2009
|
|
--- blob/src/bin/psql/large_obj.c Fri Dec 18 09:40:55 2009
|
|
*************** do_lo_list(void)
|
|
*** 278,290 ****
|
|
char buf[1024];
|
|
printQueryOpt myopt = pset.popt;
|
|
|
|
! snprintf(buf, sizeof(buf),
|
|
! "SELECT loid as \"%s\",\n"
|
|
! " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
|
|
! "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
|
|
! "ORDER BY 1",
|
|
! gettext_noop("ID"),
|
|
! gettext_noop("Description"));
|
|
|
|
res = PSQLexec(buf, false);
|
|
if (!res)
|
|
--- 278,305 ----
|
|
char buf[1024];
|
|
printQueryOpt myopt = pset.popt;
|
|
|
|
! if (pset.sversion >= 80500)
|
|
! {
|
|
! snprintf(buf, sizeof(buf),
|
|
! "SELECT oid as \"%s\",\n"
|
|
! " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n"
|
|
! " pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
|
|
! " FROM pg_catalog.pg_largeobject_metadata "
|
|
! " ORDER BY oid",
|
|
! gettext_noop("ID"),
|
|
! gettext_noop("Owner"),
|
|
! gettext_noop("Description"));
|
|
! }
|
|
! else
|
|
! {
|
|
! snprintf(buf, sizeof(buf),
|
|
! "SELECT loid as \"%s\",\n"
|
|
! " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
|
|
! "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
|
|
! "ORDER BY 1",
|
|
! gettext_noop("ID"),
|
|
! gettext_noop("Description"));
|
|
! }
|
|
|
|
res = PSQLexec(buf, false);
|
|
if (!res)
|
|
diff -Nrpc base/src/bin/psql/tab-complete.c blob/src/bin/psql/tab-complete.c
|
|
*** base/src/bin/psql/tab-complete.c Thu Jun 18 10:20:52 2009
|
|
--- blob/src/bin/psql/tab-complete.c Fri Dec 18 09:40:55 2009
|
|
*************** psql_completion(char *text, int start, i
|
|
*** 693,699 ****
|
|
{
|
|
static const char *const list_ALTER[] =
|
|
{"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
|
|
! "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
|
|
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
|
|
|
|
COMPLETE_WITH_LIST(list_ALTER);
|
|
--- 693,699 ----
|
|
{
|
|
static const char *const list_ALTER[] =
|
|
{"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
|
|
! "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
|
|
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
|
|
|
|
COMPLETE_WITH_LIST(list_ALTER);
|
|
*************** psql_completion(char *text, int start, i
|
|
*** 762,767 ****
|
|
--- 762,778 ----
|
|
COMPLETE_WITH_LIST(list_ALTERLANGUAGE);
|
|
}
|
|
|
|
+ /* ALTER LARGE OBJECT <oid> */
|
|
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
|
|
+ pg_strcasecmp(prev3_wd, "LARGE") == 0 &&
|
|
+ pg_strcasecmp(prev2_wd, "OBJECT") == 0)
|
|
+ {
|
|
+ static const char *const list_ALTERLARGEOBJECT[] =
|
|
+ {"OWNER TO", NULL};
|
|
+
|
|
+ COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT);
|
|
+ }
|
|
+
|
|
/* ALTER USER,ROLE <name> */
|
|
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
|
|
!(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) &&
|
|
*************** psql_completion(char *text, int start, i
|
|
*** 1703,1708 ****
|
|
--- 1714,1720 ----
|
|
" UNION SELECT 'FOREIGN SERVER'"
|
|
" UNION SELECT 'FUNCTION'"
|
|
" UNION SELECT 'LANGUAGE'"
|
|
+ " UNION SELECT 'LARGE OBJECT'"
|
|
" UNION SELECT 'SCHEMA'"
|
|
" UNION SELECT 'TABLESPACE'");
|
|
|
|
diff -Nrpc base/src/include/catalog/catversion.h blob/src/include/catalog/catversion.h
|
|
*** base/src/include/catalog/catversion.h Thu Jun 18 10:20:52 2009
|
|
--- blob/src/include/catalog/catversion.h Fri Dec 18 09:40:55 2009
|
|
***************
|
|
*** 53,58 ****
|
|
*/
|
|
|
|
/* yyyymmddN */
|
|
! #define CATALOG_VERSION_NO 200904091
|
|
|
|
#endif
|
|
--- 53,58 ----
|
|
*/
|
|
|
|
/* yyyymmddN */
|
|
! #define CATALOG_VERSION_NO 200912151
|
|
|
|
#endif
|
|
diff -Nrpc base/src/include/catalog/dependency.h blob/src/include/catalog/dependency.h
|
|
*** base/src/include/catalog/dependency.h Thu Jun 18 10:20:52 2009
|
|
--- blob/src/include/catalog/dependency.h Fri Dec 18 09:40:55 2009
|
|
*************** typedef enum ObjectClass
|
|
*** 128,133 ****
|
|
--- 128,134 ----
|
|
OCLASS_CONVERSION, /* pg_conversion */
|
|
OCLASS_DEFAULT, /* pg_attrdef */
|
|
OCLASS_LANGUAGE, /* pg_language */
|
|
+ OCLASS_LARGEOBJECT, /* pg_largeobject */
|
|
OCLASS_OPERATOR, /* pg_operator */
|
|
OCLASS_OPCLASS, /* pg_opclass */
|
|
OCLASS_OPFAMILY, /* pg_opfamily */
|
|
diff -Nrpc base/src/include/catalog/indexing.h blob/src/include/catalog/indexing.h
|
|
*** base/src/include/catalog/indexing.h Thu Jun 18 10:20:52 2009
|
|
--- blob/src/include/catalog/indexing.h Fri Dec 18 09:40:55 2009
|
|
*************** DECLARE_UNIQUE_INDEX(pg_language_oid_ind
|
|
*** 165,170 ****
|
|
--- 165,173 ----
|
|
DECLARE_UNIQUE_INDEX(pg_largeobject_loid_pn_index, 2683, on pg_largeobject using btree(loid oid_ops, pageno int4_ops));
|
|
#define LargeObjectLOidPNIndexId 2683
|
|
|
|
+ DECLARE_UNIQUE_INDEX(pg_largeobject_metadata_oid_index, 2996, on pg_largeobject_metadata using btree(oid oid_ops));
|
|
+ #define LargeObjectMetadataOidIndexId 2996
|
|
+
|
|
DECLARE_UNIQUE_INDEX(pg_namespace_nspname_index, 2684, on pg_namespace using btree(nspname name_ops));
|
|
#define NamespaceNameIndexId 2684
|
|
DECLARE_UNIQUE_INDEX(pg_namespace_oid_index, 2685, on pg_namespace using btree(oid oid_ops));
|
|
diff -Nrpc base/src/include/catalog/pg_largeobject.h blob/src/include/catalog/pg_largeobject.h
|
|
*** base/src/include/catalog/pg_largeobject.h Sat Jan 3 12:25:21 2009
|
|
--- blob/src/include/catalog/pg_largeobject.h Fri Dec 18 09:40:55 2009
|
|
*************** typedef FormData_pg_largeobject *Form_pg
|
|
*** 51,58 ****
|
|
#define Anum_pg_largeobject_pageno 2
|
|
#define Anum_pg_largeobject_data 3
|
|
|
|
! extern void LargeObjectCreate(Oid loid);
|
|
extern void LargeObjectDrop(Oid loid);
|
|
extern bool LargeObjectExists(Oid loid);
|
|
|
|
#endif /* PG_LARGEOBJECT_H */
|
|
--- 51,59 ----
|
|
#define Anum_pg_largeobject_pageno 2
|
|
#define Anum_pg_largeobject_data 3
|
|
|
|
! extern Oid LargeObjectCreate(Oid loid);
|
|
extern void LargeObjectDrop(Oid loid);
|
|
+ extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId);
|
|
extern bool LargeObjectExists(Oid loid);
|
|
|
|
#endif /* PG_LARGEOBJECT_H */
|
|
diff -Nrpc base/src/include/catalog/pg_largeobject_metadata.h blob/src/include/catalog/pg_largeobject_metadata.h
|
|
*** base/src/include/catalog/pg_largeobject_metadata.h Thu Jan 1 09:00:00 1970
|
|
--- blob/src/include/catalog/pg_largeobject_metadata.h Fri Dec 18 09:41:26 2009
|
|
***************
|
|
*** 0 ****
|
|
--- 1,52 ----
|
|
+ /*-------------------------------------------------------------------------
|
|
+ *
|
|
+ * pg_largeobject_metadata.h
|
|
+ * definition of the system "largeobject_metadata" relation (pg_largeobject_metadata)
|
|
+ * along with the relation's initial contents.
|
|
+ *
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ *
|
|
+ * $PostgreSQL$
|
|
+ *
|
|
+ * NOTES
|
|
+ * the genbki.sh script reads this file and generates .bki
|
|
+ * information from the DATA() statements.
|
|
+ *
|
|
+ *-------------------------------------------------------------------------
|
|
+ */
|
|
+ #ifndef PG_LARGEOBJECT_METADATA_H
|
|
+ #define PG_LARGEOBJECT_METADATA_H
|
|
+
|
|
+ #include "catalog/genbki.h"
|
|
+
|
|
+ /* ----------------
|
|
+ * pg_largeobject_metadata definition. cpp turns this into
|
|
+ * typedef struct FormData_pg_largeobject_metadata
|
|
+ * ----------------
|
|
+ */
|
|
+ #define LargeObjectMetadataRelationId 2995
|
|
+
|
|
+ CATALOG(pg_largeobject_metadata,2995)
|
|
+ {
|
|
+ Oid lomowner; /* OID of the largeobject owner */
|
|
+ aclitem lomacl[1]; /* access permissions */
|
|
+ } FormData_pg_largeobject_metadata;
|
|
+
|
|
+ /* ----------------
|
|
+ * Form_pg_largeobject_metadata corresponds to a pointer to a tuple
|
|
+ * with the format of pg_largeobject_metadata relation.
|
|
+ * ----------------
|
|
+ */
|
|
+ typedef FormData_pg_largeobject_metadata *Form_pg_largeobject_metadata;
|
|
+
|
|
+ /* ----------------
|
|
+ * compiler constants for pg_largeobject_metadata
|
|
+ * ----------------
|
|
+ */
|
|
+ #define Natts_pg_largeobject_metadata 2
|
|
+ #define Anum_pg_largeobject_metadata_lomowner 1
|
|
+ #define Anum_pg_largeobject_metadata_lomacl 2
|
|
+
|
|
+ #endif /* PG_LARGEOBJECT_METADATA_H */
|
|
diff -Nrpc base/src/include/libpq/be-fsstubs.h blob/src/include/libpq/be-fsstubs.h
|
|
*** base/src/include/libpq/be-fsstubs.h Sat Jan 3 12:25:21 2009
|
|
--- blob/src/include/libpq/be-fsstubs.h Fri Dec 18 09:40:55 2009
|
|
*************** extern Datum lo_unlink(PG_FUNCTION_ARGS)
|
|
*** 38,43 ****
|
|
--- 38,48 ----
|
|
extern Datum lo_truncate(PG_FUNCTION_ARGS);
|
|
|
|
/*
|
|
+ * compatibility option for access control
|
|
+ */
|
|
+ extern bool lo_compat_privileges;
|
|
+
|
|
+ /*
|
|
* These are not fmgr-callable, but are available to C code.
|
|
* Probably these should have had the underscore-free names,
|
|
* but too late now...
|
|
diff -Nrpc base/src/include/nodes/parsenodes.h blob/src/include/nodes/parsenodes.h
|
|
*** base/src/include/nodes/parsenodes.h Tue Dec 15 17:16:51 2009
|
|
--- blob/src/include/nodes/parsenodes.h Fri Dec 18 09:40:55 2009
|
|
*************** typedef enum GrantObjectType
|
|
*** 1186,1191 ****
|
|
--- 1186,1192 ----
|
|
ACL_OBJECT_FOREIGN_SERVER, /* foreign server */
|
|
ACL_OBJECT_FUNCTION, /* function */
|
|
ACL_OBJECT_LANGUAGE, /* procedural language */
|
|
+ ACL_OBJECT_LARGEOBJECT, /* largeobject */
|
|
ACL_OBJECT_NAMESPACE, /* namespace */
|
|
ACL_OBJECT_TABLESPACE /* tablespace */
|
|
} GrantObjectType;
|
|
diff -Nrpc base/src/include/utils/acl.h blob/src/include/utils/acl.h
|
|
*** base/src/include/utils/acl.h Thu Jun 18 10:20:52 2009
|
|
--- blob/src/include/utils/acl.h Fri Dec 18 09:40:55 2009
|
|
***************
|
|
*** 26,31 ****
|
|
--- 26,32 ----
|
|
|
|
#include "nodes/parsenodes.h"
|
|
#include "utils/array.h"
|
|
+ #include "utils/snapshot.h"
|
|
|
|
|
|
/*
|
|
*************** typedef ArrayType Acl;
|
|
*** 151,156 ****
|
|
--- 152,158 ----
|
|
#define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE)
|
|
#define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE)
|
|
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE)
|
|
+ #define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE)
|
|
#define ACL_ALL_RIGHTS_NAMESPACE (ACL_USAGE|ACL_CREATE)
|
|
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
|
|
|
|
*************** typedef enum AclObjectKind
|
|
*** 181,186 ****
|
|
--- 183,189 ----
|
|
ACL_KIND_OPER, /* pg_operator */
|
|
ACL_KIND_TYPE, /* pg_type */
|
|
ACL_KIND_LANGUAGE, /* pg_language */
|
|
+ ACL_KIND_LARGEOBJECT, /* pg_largeobject */
|
|
ACL_KIND_NAMESPACE, /* pg_namespace */
|
|
ACL_KIND_OPCLASS, /* pg_opclass */
|
|
ACL_KIND_OPFAMILY, /* pg_opfamily */
|
|
*************** extern AclMode pg_proc_aclmask(Oid proc_
|
|
*** 273,278 ****
|
|
--- 276,283 ----
|
|
AclMode mask, AclMaskHow how);
|
|
extern AclMode pg_language_aclmask(Oid lang_oid, Oid roleid,
|
|
AclMode mask, AclMaskHow how);
|
|
+ extern AclMode pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
|
|
+ AclMode mask, AclMaskHow how, Snapshot snapshot);
|
|
extern AclMode pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
|
|
AclMode mask, AclMaskHow how);
|
|
extern AclMode pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
|
|
*************** extern AclResult pg_class_aclcheck(Oid t
|
|
*** 290,295 ****
|
|
--- 295,302 ----
|
|
extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode);
|
|
extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode);
|
|
extern AclResult pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode);
|
|
+ extern AclResult pg_largeobject_aclcheck_snapshot(Oid lang_oid, Oid roleid,
|
|
+ AclMode mode, Snapshot snapshot);
|
|
extern AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode);
|
|
extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
|
|
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
|
|
*************** extern bool pg_type_ownercheck(Oid type_
|
|
*** 307,312 ****
|
|
--- 314,320 ----
|
|
extern bool pg_oper_ownercheck(Oid oper_oid, Oid roleid);
|
|
extern bool pg_proc_ownercheck(Oid proc_oid, Oid roleid);
|
|
extern bool pg_language_ownercheck(Oid lan_oid, Oid roleid);
|
|
+ extern bool pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid);
|
|
extern bool pg_namespace_ownercheck(Oid nsp_oid, Oid roleid);
|
|
extern bool pg_tablespace_ownercheck(Oid spc_oid, Oid roleid);
|
|
extern bool pg_opclass_ownercheck(Oid opc_oid, Oid roleid);
|
|
diff -Nrpc base/src/test/regress/expected/privileges.out blob/src/test/regress/expected/privileges.out
|
|
*** base/src/test/regress/expected/privileges.out Fri Mar 6 09:45:33 2009
|
|
--- blob/src/test/regress/expected/privileges.out Fri Dec 18 09:40:55 2009
|
|
*************** DROP ROLE IF EXISTS regressuser2;
|
|
*** 11,16 ****
|
|
--- 11,22 ----
|
|
DROP ROLE IF EXISTS regressuser3;
|
|
DROP ROLE IF EXISTS regressuser4;
|
|
DROP ROLE IF EXISTS regressuser5;
|
|
+ DROP ROLE IF EXISTS regressuser6;
|
|
+ SELECT lo_unlink(oid) FROM pg_largeobject_metadata;
|
|
+ lo_unlink
|
|
+ -----------
|
|
+ (0 rows)
|
|
+
|
|
RESET client_min_messages;
|
|
-- test proper begins here
|
|
CREATE USER regressuser1;
|
|
*************** SELECT has_table_privilege('regressuser1
|
|
*** 815,820 ****
|
|
--- 821,1014 ----
|
|
t
|
|
(1 row)
|
|
|
|
+ -- largeobject privilege tests
|
|
+ \c -
|
|
+ SET SESSION AUTHORIZATION regressuser1;
|
|
+ SELECT lo_create(1001);
|
|
+ lo_create
|
|
+ -----------
|
|
+ 1001
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lo_create(1002);
|
|
+ lo_create
|
|
+ -----------
|
|
+ 1002
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lo_create(1003);
|
|
+ lo_create
|
|
+ -----------
|
|
+ 1003
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lo_create(1004);
|
|
+ lo_create
|
|
+ -----------
|
|
+ 1004
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lo_create(1005);
|
|
+ lo_create
|
|
+ -----------
|
|
+ 1005
|
|
+ (1 row)
|
|
+
|
|
+ GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC;
|
|
+ GRANT SELECT ON LARGE OBJECT 1003 TO regressuser2;
|
|
+ GRANT SELECT,UPDATE ON LARGE OBJECT 1004 TO regressuser2;
|
|
+ GRANT ALL ON LARGE OBJECT 1005 TO regressuser2;
|
|
+ GRANT SELECT ON LARGE OBJECT 1005 TO regressuser2 WITH GRANT OPTION;
|
|
+ GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed
|
|
+ ERROR: invalid privilege type INSERT for large object
|
|
+ GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed
|
|
+ ERROR: role "nosuchuser" does not exist
|
|
+ GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed
|
|
+ ERROR: large object 999 does not exist
|
|
+ \c -
|
|
+ SET SESSION AUTHORIZATION regressuser2;
|
|
+ SELECT lo_create(2001);
|
|
+ lo_create
|
|
+ -----------
|
|
+ 2001
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lo_create(2002);
|
|
+ lo_create
|
|
+ -----------
|
|
+ 2002
|
|
+ (1 row)
|
|
+
|
|
+ SELECT loread(lo_open(1001, x'40000'::int), 32);
|
|
+ loread
|
|
+ --------
|
|
+
|
|
+ (1 row)
|
|
+
|
|
+ SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
|
|
+ ERROR: permission denied for large object 1002
|
|
+ SELECT loread(lo_open(1003, x'40000'::int), 32);
|
|
+ loread
|
|
+ --------
|
|
+
|
|
+ (1 row)
|
|
+
|
|
+ SELECT loread(lo_open(1004, x'40000'::int), 32);
|
|
+ loread
|
|
+ --------
|
|
+
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lowrite(lo_open(1001, x'20000'::int), 'abcd');
|
|
+ lowrite
|
|
+ ---------
|
|
+ 4
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied
|
|
+ ERROR: permission denied for large object 1002
|
|
+ SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied
|
|
+ ERROR: permission denied for large object 1003
|
|
+ SELECT lowrite(lo_open(1004, x'20000'::int), 'abcd');
|
|
+ lowrite
|
|
+ ---------
|
|
+ 4
|
|
+ (1 row)
|
|
+
|
|
+ GRANT SELECT ON LARGE OBJECT 1005 TO regressuser3;
|
|
+ GRANT UPDATE ON LARGE OBJECT 1006 TO regressuser3; -- to be denied
|
|
+ ERROR: large object 1006 does not exist
|
|
+ REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC;
|
|
+ GRANT ALL ON LARGE OBJECT 2001 TO regressuser3;
|
|
+ SELECT lo_unlink(1001); -- to be denied
|
|
+ ERROR: must be owner of large object 1001
|
|
+ SELECT lo_unlink(2002);
|
|
+ lo_unlink
|
|
+ -----------
|
|
+ 1
|
|
+ (1 row)
|
|
+
|
|
+ \c -
|
|
+ -- confirm ACL setting
|
|
+ SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata;
|
|
+ oid | ownername | lomacl
|
|
+ ------+--------------+------------------------------------------------------------------------------------------
|
|
+ 1002 | regressuser1 |
|
|
+ 1001 | regressuser1 | {regressuser1=rw/regressuser1,=rw/regressuser1}
|
|
+ 1003 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=r/regressuser1}
|
|
+ 1004 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=rw/regressuser1}
|
|
+ 1005 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=r*w/regressuser1,regressuser3=r/regressuser2}
|
|
+ 2001 | regressuser2 | {regressuser2=rw/regressuser2,regressuser3=rw/regressuser2}
|
|
+ (6 rows)
|
|
+
|
|
+ SET SESSION AUTHORIZATION regressuser3;
|
|
+ SELECT loread(lo_open(1001, x'40000'::int), 32);
|
|
+ loread
|
|
+ --------
|
|
+ abcd
|
|
+ (1 row)
|
|
+
|
|
+ SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied
|
|
+ ERROR: permission denied for large object 1003
|
|
+ SELECT loread(lo_open(1005, x'40000'::int), 32);
|
|
+ loread
|
|
+ --------
|
|
+
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied
|
|
+ ERROR: permission denied for large object 1005
|
|
+ SELECT lo_truncate(lo_open(2001, x'20000'::int), 10);
|
|
+ lo_truncate
|
|
+ -------------
|
|
+ 0
|
|
+ (1 row)
|
|
+
|
|
+ -- compatibility mode in largeobject permission
|
|
+ \c -
|
|
+ SET lo_compat_privileges = false; -- default setting
|
|
+ SET SESSION AUTHORIZATION regressuser4;
|
|
+ SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
|
|
+ ERROR: permission denied for large object 1002
|
|
+ SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied
|
|
+ ERROR: permission denied for large object 1002
|
|
+ SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied
|
|
+ ERROR: permission denied for large object 1002
|
|
+ SELECT lo_unlink(1002); -- to be denied
|
|
+ ERROR: must be owner of large object 1002
|
|
+ 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.
|
|
+ \c -
|
|
+ SET lo_compat_privileges = true; -- compatibility mode
|
|
+ SET SESSION AUTHORIZATION regressuser4;
|
|
+ SELECT loread(lo_open(1002, x'40000'::int), 32);
|
|
+ loread
|
|
+ --------
|
|
+
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd');
|
|
+ lowrite
|
|
+ ---------
|
|
+ 4
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lo_truncate(lo_open(1002, x'20000'::int), 10);
|
|
+ lo_truncate
|
|
+ -------------
|
|
+ 0
|
|
+ (1 row)
|
|
+
|
|
+ SELECT lo_unlink(1002);
|
|
+ lo_unlink
|
|
+ -----------
|
|
+ 1
|
|
+ (1 row)
|
|
+
|
|
+ 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.
|
|
-- clean up
|
|
\c
|
|
DROP FUNCTION testfunc2(int);
|
|
*************** DROP TABLE atest6;
|
|
*** 836,841 ****
|
|
--- 1030,1045 ----
|
|
DROP TABLE atestc;
|
|
DROP TABLE atestp1;
|
|
DROP TABLE atestp2;
|
|
+ SELECT lo_unlink(oid) FROM pg_largeobject_metadata;
|
|
+ lo_unlink
|
|
+ -----------
|
|
+ 1
|
|
+ 1
|
|
+ 1
|
|
+ 1
|
|
+ 1
|
|
+ (5 rows)
|
|
+
|
|
DROP GROUP regressgroup1;
|
|
DROP GROUP regressgroup2;
|
|
REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
|
|
*************** DROP USER regressuser2;
|
|
*** 844,846 ****
|
|
--- 1048,1052 ----
|
|
DROP USER regressuser3;
|
|
DROP USER regressuser4;
|
|
DROP USER regressuser5;
|
|
+ DROP USER regressuser6;
|
|
+ ERROR: role "regressuser6" does not exist
|
|
diff -Nrpc base/src/test/regress/expected/sanity_check.out blob/src/test/regress/expected/sanity_check.out
|
|
*** base/src/test/regress/expected/sanity_check.out Tue Feb 10 10:10:02 2009
|
|
--- blob/src/test/regress/expected/sanity_check.out Fri Dec 18 09:40:55 2009
|
|
*************** SELECT relname, relhasindex
|
|
*** 104,109 ****
|
|
--- 104,110 ----
|
|
pg_inherits | t
|
|
pg_language | t
|
|
pg_largeobject | t
|
|
+ pg_largeobject_metadata | t
|
|
pg_listener | f
|
|
pg_namespace | t
|
|
pg_opclass | t
|
|
*************** SELECT relname, relhasindex
|
|
*** 151,157 ****
|
|
timetz_tbl | f
|
|
tinterval_tbl | f
|
|
varchar_tbl | f
|
|
! (140 rows)
|
|
|
|
--
|
|
-- another sanity check: every system catalog that has OIDs should have
|
|
--- 152,158 ----
|
|
timetz_tbl | f
|
|
tinterval_tbl | f
|
|
varchar_tbl | f
|
|
! (141 rows)
|
|
|
|
--
|
|
-- another sanity check: every system catalog that has OIDs should have
|
|
diff -Nrpc base/src/test/regress/sql/privileges.sql blob/src/test/regress/sql/privileges.sql
|
|
*** base/src/test/regress/sql/privileges.sql Fri Mar 6 09:45:33 2009
|
|
--- blob/src/test/regress/sql/privileges.sql Fri Dec 18 09:40:55 2009
|
|
*************** DROP ROLE IF EXISTS regressuser2;
|
|
*** 15,20 ****
|
|
--- 15,23 ----
|
|
DROP ROLE IF EXISTS regressuser3;
|
|
DROP ROLE IF EXISTS regressuser4;
|
|
DROP ROLE IF EXISTS regressuser5;
|
|
+ DROP ROLE IF EXISTS regressuser6;
|
|
+
|
|
+ SELECT lo_unlink(oid) FROM pg_largeobject_metadata;
|
|
|
|
RESET client_min_messages;
|
|
|
|
*************** ALTER GROUP regressgroup2 ADD USER regre
|
|
*** 36,42 ****
|
|
ALTER GROUP regressgroup2 DROP USER regressuser2;
|
|
ALTER GROUP regressgroup2 ADD USER regressuser4;
|
|
|
|
-
|
|
-- test owner privileges
|
|
|
|
SET SESSION AUTHORIZATION regressuser1;
|
|
--- 39,44 ----
|
|
*************** SELECT has_table_privilege('regressuser3
|
|
*** 468,473 ****
|
|
--- 470,552 ----
|
|
|
|
SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true
|
|
|
|
+ -- largeobject privilege tests
|
|
+ \c -
|
|
+ SET SESSION AUTHORIZATION regressuser1;
|
|
+
|
|
+ SELECT lo_create(1001);
|
|
+ SELECT lo_create(1002);
|
|
+ SELECT lo_create(1003);
|
|
+ SELECT lo_create(1004);
|
|
+ SELECT lo_create(1005);
|
|
+
|
|
+ GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC;
|
|
+ GRANT SELECT ON LARGE OBJECT 1003 TO regressuser2;
|
|
+ GRANT SELECT,UPDATE ON LARGE OBJECT 1004 TO regressuser2;
|
|
+ GRANT ALL ON LARGE OBJECT 1005 TO regressuser2;
|
|
+ GRANT SELECT ON LARGE OBJECT 1005 TO regressuser2 WITH GRANT OPTION;
|
|
+
|
|
+ GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed
|
|
+ GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed
|
|
+ GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed
|
|
+
|
|
+ \c -
|
|
+ SET SESSION AUTHORIZATION regressuser2;
|
|
+
|
|
+ SELECT lo_create(2001);
|
|
+ SELECT lo_create(2002);
|
|
+
|
|
+ SELECT loread(lo_open(1001, x'40000'::int), 32);
|
|
+ SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
|
|
+ SELECT loread(lo_open(1003, x'40000'::int), 32);
|
|
+ SELECT loread(lo_open(1004, x'40000'::int), 32);
|
|
+
|
|
+ SELECT lowrite(lo_open(1001, x'20000'::int), 'abcd');
|
|
+ SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied
|
|
+ SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied
|
|
+ SELECT lowrite(lo_open(1004, x'20000'::int), 'abcd');
|
|
+
|
|
+ GRANT SELECT ON LARGE OBJECT 1005 TO regressuser3;
|
|
+ GRANT UPDATE ON LARGE OBJECT 1006 TO regressuser3; -- to be denied
|
|
+ REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC;
|
|
+ GRANT ALL ON LARGE OBJECT 2001 TO regressuser3;
|
|
+
|
|
+ SELECT lo_unlink(1001); -- to be denied
|
|
+ SELECT lo_unlink(2002);
|
|
+
|
|
+ \c -
|
|
+ -- confirm ACL setting
|
|
+ SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata;
|
|
+
|
|
+ SET SESSION AUTHORIZATION regressuser3;
|
|
+
|
|
+ SELECT loread(lo_open(1001, x'40000'::int), 32);
|
|
+ SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied
|
|
+ SELECT loread(lo_open(1005, x'40000'::int), 32);
|
|
+
|
|
+ SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied
|
|
+ SELECT lo_truncate(lo_open(2001, x'20000'::int), 10);
|
|
+
|
|
+ -- compatibility mode in largeobject permission
|
|
+ \c -
|
|
+ SET lo_compat_privileges = false; -- default setting
|
|
+ SET SESSION AUTHORIZATION regressuser4;
|
|
+
|
|
+ SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
|
|
+ SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied
|
|
+ SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied
|
|
+ SELECT lo_unlink(1002); -- to be denied
|
|
+ SELECT lo_export(1001, '/dev/null'); -- to be denied
|
|
+
|
|
+ \c -
|
|
+ SET lo_compat_privileges = true; -- compatibility mode
|
|
+ SET SESSION AUTHORIZATION regressuser4;
|
|
+
|
|
+ SELECT loread(lo_open(1002, x'40000'::int), 32);
|
|
+ SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd');
|
|
+ SELECT lo_truncate(lo_open(1002, x'20000'::int), 10);
|
|
+ SELECT lo_unlink(1002);
|
|
+ SELECT lo_export(1001, '/dev/null'); -- to be denied
|
|
|
|
-- clean up
|
|
|
|
*************** DROP TABLE atestc;
|
|
*** 493,498 ****
|
|
--- 572,579 ----
|
|
DROP TABLE atestp1;
|
|
DROP TABLE atestp2;
|
|
|
|
+ SELECT lo_unlink(oid) FROM pg_largeobject_metadata;
|
|
+
|
|
DROP GROUP regressgroup1;
|
|
DROP GROUP regressgroup2;
|
|
|
|
*************** DROP USER regressuser2;
|
|
*** 502,504 ****
|
|
--- 583,586 ----
|
|
DROP USER regressuser3;
|
|
DROP USER regressuser4;
|
|
DROP USER regressuser5;
|
|
+ DROP USER regressuser6;
|