diff -Nrpc base/doc/src/sgml/ref/allfiles.sgml blob/doc/src/sgml/ref/allfiles.sgml
*** base/doc/src/sgml/ref/allfiles.sgml Wed Dec 24 11:45:25 2008
--- blob/doc/src/sgml/ref/allfiles.sgml Mon Aug 31 12:57:21 2009
*************** Complete list of usable sgml source file
*** 15,20 ****
--- 15,21 ----
+
diff -Nrpc base/doc/src/sgml/ref/alter_large_object.sgml blob/doc/src/sgml/ref/alter_large_object.sgml
*** base/doc/src/sgml/ref/alter_large_object.sgml Thu Jan 1 09:00:00 1970
--- blob/doc/src/sgml/ref/alter_large_object.sgml Mon Aug 31 12:57:21 2009
***************
*** 0 ****
--- 1,75 ----
+
+
+ ALTER LARGE OBJECT
+ 7
+ SQL - Language Statements
+
+
+
+ ALTER LARGE OBJECT
+ change the definition of a largeobject
+
+
+
+ ALTER LARGE OBJECT
+
+
+
+
+ ALTER LARGE OBJECT large_object_oid OWNER TO new_owner
+
+
+
+
+ Description
+
+
+ ALTER LARGE OBJECT changes the definition of a
+ largeobject. The only functionality is to assign a new owner.
+ You must be superuser or owner of the largeobject to use
+ ALTER LARGE OBJECT.
+
+
+
+
+ Parameters
+
+
+
+ large_object_oid
+
+
+ OID of the largeobject to be altered
+
+
+
+
+
+ new_owner
+
+
+ The new owner of the largeobject
+
+
+
+
+
+
+
+ Compatibility
+
+
+ There is no ALTER LARGE OBJECT statement in the SQL
+ standard.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
diff -Nrpc base/doc/src/sgml/ref/grant.sgml blob/doc/src/sgml/ref/grant.sgml
*** base/doc/src/sgml/ref/grant.sgml Fri Jan 23 10:23:37 2009
--- blob/doc/src/sgml/ref/grant.sgml Thu Aug 27 11:31:24 2009
*************** GRANT { USAGE | ALL [ PRIVILEGES ] }
*** 56,61 ****
--- 56,65 ----
ON LANGUAGE langname [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+ GRANT { { SELECT | UPDATE } [,...] | ALL [ PRIVILEGES ] }
+ ON LARGE OBJECT loid [, ...]
+ TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
ON SCHEMA schemaname [, ...]
TO { [ GROUP ] rolename | PUBLIC } [, ...] [ WITH GRANT OPTION ]
*************** GRANT rol
*** 160,165 ****
--- 164,171 ----
.
For sequences, this privilege also allows the use of the
currval function.
+ For largeobjects, this privilege also allows to read from
+ the target largeobject.
*************** GRANT rol
*** 193,198 ****
--- 199,206 ----
SELECT privilege. For sequences, this
privilege allows the use of the nextval and
setval functions.
+ For largeobjects, this privilege also allows to write or truncate
+ on the target largeobject.
diff -Nrpc base/doc/src/sgml/ref/revoke.sgml blob/doc/src/sgml/ref/revoke.sgml
*** base/doc/src/sgml/ref/revoke.sgml Fri Jan 23 10:23:37 2009
--- blob/doc/src/sgml/ref/revoke.sgml Thu Aug 27 11:31:24 2009
*************** REVOKE [ GRANT OPTION FOR ]
*** 73,78 ****
--- 73,84 ----
[ CASCADE | RESTRICT ]
REVOKE [ GRANT OPTION FOR ]
+ { { SELECT | UPDATE } [,...] | ALL [ PRIVILEGES ] }
+ ON LARGE OBJECT loid [, ...]
+ FROM { [ GROUP ] rolename | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
+ REVOKE [ GRANT OPTION FOR ]
{ { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
ON SCHEMA schemaname [, ...]
FROM { [ GROUP ] rolename | PUBLIC } [, ...]
diff -Nrpc base/doc/src/sgml/reference.sgml blob/doc/src/sgml/reference.sgml
*** base/doc/src/sgml/reference.sgml Wed Dec 24 11:45:25 2008
--- blob/doc/src/sgml/reference.sgml Mon Aug 31 12:57:21 2009
***************
*** 43,48 ****
--- 43,49 ----
&alterGroup;
&alterIndex;
&alterLanguage;
+ &alterLargeObject;
&alterOperator;
&alterOperatorClass;
&alterOperatorFamily;
diff -Nrpc base/src/backend/catalog/Makefile blob/src/backend/catalog/Makefile
*** base/src/backend/catalog/Makefile Mon Aug 31 09:27:20 2009
--- blob/src/backend/catalog/Makefile Mon Aug 31 10:48:15 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_meta.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 Jun 18 10:20:52 2009
--- blob/src/backend/catalog/aclchk.c Sun Aug 30 00:43:51 2009
***************
*** 30,35 ****
--- 30,36 ----
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
+ #include "catalog/pg_largeobject_meta.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
*************** static void ExecGrant_Fdw(InternalGrant
*** 57,62 ****
--- 58,64 ----
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 ****
--- 202,210 ----
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)
*** 344,349 ****
--- 349,358 ----
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 largeobject");
+ break;
case ACL_OBJECT_NAMESPACE:
all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
errormsg = gettext_noop("invalid privilege type %s for schema");
*************** ExecGrantStmt_oids(InternalGrant *istmt)
*** 449,454 ****
--- 458,466 ----
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
*** 533,538 ****
--- 545,566 ----
ReleaseSysCache(tuple);
}
break;
+ case ACL_OBJECT_LARGEOBJECT:
+ foreach(cell, objnames)
+ {
+ Oid lobjOid = intVal(lfirst(cell));
+
+ if (!SearchSysCacheExists(LARGEOBJECTOID,
+ ObjectIdGetDatum(lobjOid),
+ 0, 0, 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("largeobject %u does not exist",
+ lobjOid)));
+
+ objects = lappend_oid(objects, lobjOid);
+ }
+ break;
case ACL_OBJECT_NAMESPACE:
foreach(cell, objnames)
{
*************** ExecGrant_Language(InternalGrant *istmt)
*** 1746,1751 ****
--- 1774,1896 ----
}
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(LargeObjectMetaRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid loid = lfirst_oid(cell);
+ char loname[NAMEDATALEN];
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_largeobject_meta];
+ bool nulls[Natts_pg_largeobject_meta];
+ bool replaces[Natts_pg_largeobject_meta];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache(LARGEOBJECTOID,
+ ObjectIdGetDatum(loid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for largeobject %u", loid);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = ((Form_pg_largeobject_meta) GETSTRUCT(tuple))->lomowner;
+ snprintf(loname, sizeof(loname), "largeobject %u", loid);
+ aclDatum = SysCacheGetAttr(LARGEOBJECTOID, tuple,
+ Anum_pg_largeobject_meta_lomacl,
+ &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.
+ */
+ 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_meta_lomacl - 1] = true;
+ values[Anum_pg_largeobject_meta_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(LargeObjectMetaRelationId,
+ HeapTupleGetOid(tuple), 0,
+ ownerId, istmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ 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
*** 2085,2090 ****
--- 2230,2237 ----
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 largeobject %s"),
/* ACL_KIND_NAMESPACE */
gettext_noop("permission denied for schema %s"),
/* ACL_KIND_OPCLASS */
*************** static const char *const not_owner_msg[M
*** 2123,2128 ****
--- 2270,2277 ----
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 largeobject %s"),
/* ACL_KIND_NAMESPACE */
gettext_noop("must be owner of schema %s"),
/* ACL_KIND_OPCLASS */
*************** pg_aclmask(AclObjectKind objkind, Oid ta
*** 2242,2247 ****
--- 2391,2398 ----
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(table_oid, roleid, mask, how);
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
*** 2625,2630 ****
--- 2776,2837 ----
}
/*
+ * Exported routine for examining a user's privileges for a largeobject
+ */
+ AclMode
+ pg_largeobject_aclmask(Oid lobj_oid, Oid roleid,
+ AclMode mask, AclMaskHow how)
+ {
+ AclMode result;
+ 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
+ */
+ tuple = SearchSysCache(LARGEOBJECTOID,
+ ObjectIdGetDatum(lobj_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("largeobject %u does not exist", lobj_oid)));
+
+ ownerId = ((Form_pg_largeobject_meta) GETSTRUCT(tuple))->lomowner;
+
+ aclDatum = SysCacheGetAttr(LARGEOBJECTOID, tuple,
+ Anum_pg_largeobject_meta_lomacl, &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);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+ }
+
+ /*
* Exported routine for examining a user's privileges for a namespace
*/
AclMode
*************** pg_language_aclcheck(Oid lang_oid, Oid r
*** 3075,3080 ****
--- 3282,3299 ----
}
/*
+ * Exported routine for checking a user's access privileges to a largeobject
+ */
+ AclResult
+ pg_largeobject_aclcheck(Oid lobj_oid, Oid roleid, AclMode mode)
+ {
+ if (pg_largeobject_aclmask(lobj_oid, roleid, mode, ACLMASK_ANY) != 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
*** 3265,3270 ****
--- 3484,3517 ----
}
/*
+ * Ownership check for a largeobject (specified by OID)
+ */
+ bool
+ pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid)
+ {
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache(LARGEOBJECTOID,
+ ObjectIdGetDatum(lobj_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("largeobject %u does not exist", lobj_oid)));
+
+ ownerId = ((Form_pg_largeobject_meta) GETSTRUCT(tuple))->lomowner;
+
+ ReleaseSysCache(tuple);
+
+ 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 Thu Aug 20 22:07:11 2009
--- blob/src/backend/catalog/dependency.c Sun Aug 30 00:43:51 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_meta.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 */
+ LargeObjectMetaRelationId, /* OCLASS_LARGEOBJECT */
OperatorRelationId, /* OCLASS_OPERATOR */
OperatorClassRelationId, /* OCLASS_OPCLASS */
OperatorFamilyRelationId, /* OCLASS_OPFAMILY */
*************** doDeletion(const ObjectAddress *object)
*** 1070,1075 ****
--- 1072,1081 ----
DropProceduralLanguageById(object->objectId);
break;
+ case OCLASS_LARGEOBJECT:
+ DropLargeObject(object->objectId);
+ break;
+
case OCLASS_OPERATOR:
RemoveOperatorById(object->objectId);
break;
*************** getObjectClass(const ObjectAddress *obje
*** 1983,1988 ****
--- 1989,1998 ----
Assert(object->objectSubId == 0);
return OCLASS_LANGUAGE;
+ case LargeObjectMetaRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_LARGEOBJECT;
+
case OperatorRelationId:
Assert(object->objectSubId == 0);
return OCLASS_OPERATOR;
*************** getObjectDescription(const ObjectAddress
*** 2231,2236 ****
--- 2241,2250 ----
ReleaseSysCache(langTup);
break;
}
+ case OCLASS_LARGEOBJECT:
+ appendStringInfo(&buffer, _("largeobject %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 Thu Aug 20 22:07:11 2009
--- blob/src/backend/catalog/pg_largeobject.c Sun Aug 30 00:43:51 2009
***************
*** 16,26 ****
--- 16,34 ----
#include "access/genam.h"
#include "access/heapam.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_meta.h"
+ #include "catalog/toasting.h"
+ #include "miscadmin.h"
+ #include "utils/acl.h"
#include "utils/bytea.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
+ #include "utils/syscache.h"
#include "utils/tqual.h"
***************
*** 31,139 ****
* 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;
}
--- 39,299 ----
* appear to exist with size 0. Note that the unique index will reject
* an attempt to create a duplicate page.
*/
! Oid
! CreateLargeObject(Oid loid)
{
! Relation pg_lo_meta;
HeapTuple ntup;
! Oid loid_new;
! Datum values[Natts_pg_largeobject_meta];
! bool nulls[Natts_pg_largeobject_meta];
! pg_lo_meta = heap_open(LargeObjectMetaRelationId, RowExclusiveLock);
/*
! * Insert metadata of the largeobject
*/
! memset(values, 0, sizeof(values));
! memset(nulls, false, sizeof(nulls));
! values[Anum_pg_largeobject_meta_lomowner - 1]
! = ObjectIdGetDatum(GetUserId());
! nulls[Anum_pg_largeobject_meta_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;
}
void
! DropLargeObject(Oid loid)
{
! Relation pg_lo_meta;
Relation pg_largeobject;
ScanKeyData skey[1];
SysScanDesc sd;
HeapTuple tuple;
+ pg_lo_meta = heap_open(LargeObjectMetaRelationId, RowExclusiveLock);
+
+ pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock);
+
+ /*
+ * Delete an entry from pg_largeobject_meta
+ */
+ tuple = SearchSysCache(LARGEOBJECTOID,
+ ObjectIdGetDatum(loid),
+ 0, 0, 0);
+ 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);
+
+ ReleaseSysCache(tuple);
+
+ /*
+ * Delete all the associated entries from pg_largeobject
+ */
ScanKeyInit(&skey[0],
Anum_pg_largeobject_loid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(loid));
sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
SnapshotNow, 1, skey);
! while (HeapTupleIsValid(tuple = systable_getnext(sd)))
{
simple_heap_delete(pg_largeobject, &tuple->t_self);
}
systable_endscan(sd);
heap_close(pg_largeobject, RowExclusiveLock);
! heap_close(pg_lo_meta, RowExclusiveLock);
! }
!
! void
! AlterLargeObjectOwner(Oid loid, Oid newOwnerId)
! {
! Form_pg_largeobject_meta lomForm;
! Relation lomRel;
! HeapTuple oldtup, newtup;
!
! lomRel = heap_open(LargeObjectMetaRelationId, RowExclusiveLock);
!
! oldtup = SearchSysCache(LARGEOBJECTOID,
! ObjectIdGetDatum(loid),
! 0, 0, 0);
! if (!HeapTupleIsValid(oldtup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", loid)));
+
+ lomForm = (Form_pg_largeobject_meta) GETSTRUCT(oldtup);
+ if (lomForm->lomowner != newOwnerId)
+ {
+ Datum values[Natts_pg_largeobject_meta];
+ bool nulls[Natts_pg_largeobject_meta];
+ bool replaces[Natts_pg_largeobject_meta];
+ Acl *newAcl;
+ Datum aclDatum;
+ bool isnull;
+
+ /* Permission checks */
+ ac_largeobject_alter(loid, newOwnerId);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replaces, false, sizeof(nulls));
+
+ values[Anum_pg_largeobject_meta_lomowner - 1]
+ = ObjectIdGetDatum(newOwnerId);
+ replaces[Anum_pg_largeobject_meta_lomowner - 1] = true;
+
+ /*
+ * Determine the modified ACL for the new owner.
+ * This is only necessary when the ACL is non-null.
+ */
+ aclDatum = SysCacheGetAttr(LARGEOBJECTOID, oldtup,
+ Anum_pg_largeobject_meta_lomacl,
+ &isnull);
+ if (!isnull)
+ {
+ newAcl = aclnewowner(DatumGetAclP(aclDatum),
+ lomForm->lomowner, newOwnerId);
+ values[Anum_pg_largeobject_meta_lomacl - 1]
+ = PointerGetDatum(newAcl);
+ replaces[Anum_pg_largeobject_meta_lomacl - 1] = true;
+ }
+
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(lomRel),
+ values, nulls, replaces);
+
+ simple_heap_update(lomRel, &newtup->t_self, newtup);
+ CatalogUpdateIndexes(lomRel, newtup);
+
+ heap_freetuple(newtup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(LargeObjectMetaRelationId,
+ loid, newOwnerId);
+ }
+ ReleaseSysCache(oldtup);
+
+ heap_close(lomRel, RowExclusiveLock);
}
! /*
! * security check functions (to be moved to
! * the backend/security/access_control.c)
! */
+ void ac_largeobject_create(Oid loid)
+ {
/*
! * MEMO: In this revision, PostgreSQL implicitly allows everyone
! * to create new largeobject. It is backward compatible behavior,
! * but may be changed at the future version.
*/
! }
! void
! ac_largeobject_alter(Oid loid, Oid newOwner)
! {
! /* must be owner of largeobject */
! if (!pg_largeobject_ownercheck(loid, GetUserId()))
! ereport(ERROR,
! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be owner of largeobject %u", loid)));
! /* Superusers can always do it */
! if (OidIsValid(newOwner) && !superuser())
! {
! /* Must be able to become new owner */
! check_is_member_of_role(GetUserId(), newOwner);
! /*
! * MEMO: The new owner must have privilege to create
! * a new largeobject, and to be checked here.
! * But it is implicitly allowed to everyone, so we
! * don't put any checks in this revision.
! */
! }
! }
! void ac_largeobject_drop(Oid loid, bool dacSkip)
! {
! /* Must be owner of the largeobject */
! if (!dacSkip &&
! !pg_largeobject_ownercheck(loid, GetUserId()))
! ereport(ERROR,
! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be owner of largeobject %u", loid)));
! }
! void ac_largeobject_comment(Oid loid)
! {
! if (!pg_largeobject_ownercheck(loid, GetUserId()))
! ereport(ERROR,
! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be owner of largeobject %u", loid)));
! }
!
! void ac_largeobject_read(Oid loid)
! {
! AclResult aclresult;
!
! aclresult = pg_largeobject_aclcheck(loid, GetUserId(), ACL_SELECT);
! if (aclresult != ACLCHECK_OK)
! ereport(ERROR,
! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("permission denied for largeobject %u", loid)));
! }
!
! void ac_largeobject_write(Oid loid)
! {
! AclResult aclresult;
! aclresult = pg_largeobject_aclcheck(loid, GetUserId(), ACL_UPDATE);
! if (aclresult != ACLCHECK_OK)
! ereport(ERROR,
! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("permission denied for largeobject %u", loid)));
! }
!
! void ac_largeobject_export(Oid loid, const char *filename)
! {
! #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
! if (!superuser())
! ereport(ERROR,
! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be superuser to use server-side lo_export()"),
! errhint("Anyone can use the client-side lo_export() provided by libpq.")));
! #endif
! }
!
! void ac_largeobject_import(Oid loid, const char *filename)
! {
! #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
! if (!superuser())
! ereport(ERROR,
! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be superuser to use server-side lo_import()"),
! errhint("Anyone can use the client-side lo_import() provided by libpq.")));
! #endif
}
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 Sun Aug 30 00:43:51 2009
***************
*** 24,29 ****
--- 24,30 ----
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_language.h"
+ #include "catalog/pg_largeobject_meta.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 LargeObjectMetaRelationId:
+ 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 LargeObjectMetaRelationId:
+ AlterLargeObjectOwner(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 Sun Aug 30 00:43:51 2009
***************
*** 15,20 ****
--- 15,21 ----
#include "postgres.h"
#include "catalog/namespace.h"
+ #include "catalog/pg_largeobject_meta.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:
+ AlterLargeObjectOwner(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 Sun Aug 30 00:43:51 2009
***************
*** 24,30 ****
#include "catalog/pg_database.h"
#include "catalog/pg_description.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"
--- 24,30 ----
#include "catalog/pg_database.h"
#include "catalog/pg_description.h"
#include "catalog/pg_language.h"
! #include "catalog/pg_largeobject_meta.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
*************** CommentLargeObject(List *qualname, char
*** 1417,1429 ****
}
/* check that the large object exists */
! if (!LargeObjectExists(loid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", loid)));
/* Call CreateComments() to create/drop the comments */
! CreateComments(loid, LargeObjectRelationId, 0, comment);
}
/*
--- 1417,1437 ----
}
/* check that the large object exists */
! if (!SearchSysCacheExists(LARGEOBJECTOID,
! ObjectIdGetDatum(loid),
! 0, 0, 0))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", loid)));
+ /* Permission checks */
+ if (!pg_largeobject_ownercheck(loid, GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of largeobject %u", loid)));
+
/* Call CreateComments() to create/drop the comments */
! CreateComments(loid, LargeObjectMetaRelationId, 0, comment);
}
/*
diff -Nrpc base/src/backend/commands/tablecmds.c blob/src/backend/commands/tablecmds.c
*** base/src/backend/commands/tablecmds.c Mon Aug 31 09:27:20 2009
--- blob/src/backend/commands/tablecmds.c Mon Aug 31 10:48:15 2009
*************** ATExecAlterColumnType(AlteredTableInfo *
*** 6080,6085 ****
--- 6080,6086 ----
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 Sun Aug 30 00:43:51 2009
***************
*** 42,47 ****
--- 42,48 ----
#include
#include
+ #include "catalog/pg_largeobject_meta.h"
#include "libpq/be-fsstubs.h"
#include "libpq/libpq-fs.h"
#include "miscadmin.h"
*************** lo_read(int fd, char *buf, int len)
*** 156,161 ****
--- 157,165 ----
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd)));
+ /* Permission checks */
+ ac_largeobject_read(cookies[fd]->id);
+
status = inv_read(cookies[fd], buf, len);
return status;
*************** lo_write(int fd, const char *buf, int le
*** 177,182 ****
--- 181,189 ----
errmsg("large object descriptor %d was not opened for writing",
fd)));
+ /* Permission checks */
+ ac_largeobject_write(cookies[fd]->id);
+
status = inv_write(cookies[fd], buf, len);
return status;
*************** lo_creat(PG_FUNCTION_ARGS)
*** 212,217 ****
--- 219,227 ----
*/
CreateFSContext();
+ /* Permission check */
+ ac_largeobject_create(InvalidOid);
+
lobjId = inv_create(InvalidOid);
PG_RETURN_OID(lobjId);
*************** lo_create(PG_FUNCTION_ARGS)
*** 228,233 ****
--- 238,246 ----
*/
CreateFSContext();
+ /* Permission check */
+ ac_largeobject_create(lobjId);
+
lobjId = inv_create(lobjId);
PG_RETURN_OID(lobjId);
*************** lo_unlink(PG_FUNCTION_ARGS)
*** 251,256 ****
--- 264,272 ----
{
Oid lobjId = PG_GETARG_OID(0);
+ /* Permission check */
+ ac_largeobject_drop(lobjId, false);
+
/*
* If there are any open LO FDs referencing that ID, close 'em.
*/
*************** lo_import_internal(text *filename, Oid l
*** 346,369 ****
int nbytes,
tmp;
char buf[BUFSIZE];
! char fnamebuf[MAXPGPATH];
LargeObjectDesc *lobj;
Oid oid;
- #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to use server-side lo_import()"),
- errhint("Anyone can use the client-side lo_import() provided by libpq.")));
- #endif
-
CreateFSContext();
/*
* open the file to be read in
*/
- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666);
if (fd < 0)
ereport(ERROR,
--- 362,379 ----
int nbytes,
tmp;
char buf[BUFSIZE];
! char *fnamebuf = text_to_cstring(filename);
LargeObjectDesc *lobj;
Oid oid;
CreateFSContext();
+ /* Permission checks */
+ ac_largeobject_import(lobjOid, fnamebuf);
+
/*
* open the file to be read in
*/
fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666);
if (fd < 0)
ereport(ERROR,
*************** lo_import_internal(text *filename, Oid l
*** 395,400 ****
--- 405,411 ----
inv_close(lobj);
FileClose(fd);
+ pfree(fnamebuf);
return oid;
}
*************** lo_export(PG_FUNCTION_ARGS)
*** 412,431 ****
int nbytes,
tmp;
char buf[BUFSIZE];
! char fnamebuf[MAXPGPATH];
LargeObjectDesc *lobj;
mode_t oumask;
- #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to use server-side lo_export()"),
- errhint("Anyone can use the client-side lo_export() provided by libpq.")));
- #endif
-
CreateFSContext();
/*
* open the inversion object (no need to test for failure)
*/
--- 423,437 ----
int nbytes,
tmp;
char buf[BUFSIZE];
! char *fnamebuf = text_to_cstring(filename);
LargeObjectDesc *lobj;
mode_t oumask;
CreateFSContext();
+ /* Permission checks */
+ ac_largeobject_export(lobjId, fnamebuf);
+
/*
* open the inversion object (no need to test for failure)
*/
*************** lo_export(PG_FUNCTION_ARGS)
*** 438,444 ****
* 022. This code used to drop it all the way to 0, but creating
* world-writable export files doesn't seem wise.
*/
- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
oumask = umask((mode_t) 0022);
fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666);
umask(oumask);
--- 444,449 ----
*************** lo_export(PG_FUNCTION_ARGS)
*** 463,468 ****
--- 468,474 ----
FileClose(fd);
inv_close(lobj);
+ pfree(fnamebuf);
PG_RETURN_INT32(1);
}
*************** lo_truncate(PG_FUNCTION_ARGS)
*** 482,487 ****
--- 488,496 ----
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd)));
+ /* Permission check */
+ ac_largeobject_write(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 Thu Aug 20 22:07:11 2009
--- blob/src/backend/parser/gram.y Sun Aug 30 00:43:51 2009
*************** static TypeName *TableFuncTypeName(List
*** 386,391 ****
--- 386,392 ----
%type opt_varying opt_timezone
%type Iconst SignedIconst
+ %type Iconst_list
%type Sconst comment_text
%type RoleId opt_granted_by opt_boolean ColId_or_Sconst
%type var_list
*************** privilege_target:
*** 4406,4411 ****
--- 4407,4419 ----
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
*** 5533,5538 ****
--- 5541,5554 ----
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; }
*** 10157,10162 ****
--- 10173,10182 ----
| '-' 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 Sun Aug 30 00:43:51 2009
***************
*** 35,49 ****
--- 35,53 ----
#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_meta.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)
*** 131,173 ****
}
}
-
- /*
- * Same as pg_largeobject.c's LargeObjectExists(), except snapshot to
- * read with can be specified.
- */
- 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;
- }
-
-
static int32
getbytealen(bytea *data)
{
--- 135,140 ----
*************** getbytealen(bytea *data)
*** 177,183 ****
return (VARSIZE(data) - VARHDRSZ);
}
-
/*
* inv_create -- create a new large object
*
--- 144,149 ----
*************** 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;
}
/*
--- 159,182 ----
Oid
inv_create(Oid lobjId)
{
+ Oid lobjId_new;
+
/*
! * Create a new largeobject with empty data pages
*/
! lobjId_new = CreateLargeObject(lobjId);
/*
! * dependency on the owner of largeobject
*/
! recordDependencyOnOwner(LargeObjectMetaRelationId,
! lobjId_new, GetUserId());
/*
* Advance command counter to make new tuple visible to later operations.
*/
CommandCounterIncrement();
! return lobjId_new;
}
/*
*************** inv_open(Oid lobjId, int flags, MemoryCo
*** 233,238 ****
--- 192,204 ----
{
LargeObjectDesc *retval;
+ if (!SearchSysCacheExists(LARGEOBJECTOID,
+ ObjectIdGetDatum(lobjId),
+ 0, 0, 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist", lobjId)));
+
retval = (LargeObjectDesc *) MemoryContextAlloc(mcxt,
sizeof(LargeObjectDesc));
*************** inv_open(Oid lobjId, int flags, MemoryCo
*** 259,270 ****
else
elog(ERROR, "invalid flags: %d", flags);
- /* Can't use LargeObjectExists here because it always uses SnapshotNow */
- if (!myLargeObjectExists(lobjId, retval->snapshot))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("large object %u does not exist", lobjId)));
-
return retval;
}
--- 225,230 ----
*************** 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
--- 252,266 ----
int
inv_drop(Oid lobjId)
{
! ObjectAddress object;
! /*
! * Delete any comments and dependencies on the large object
! */
! object.classId = LargeObjectMetaRelationId;
! 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;
--- 280,285 ----
*************** 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);
--- 303,315 ----
* 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;
}
--- 324,333 ----
*************** inv_write(LargeObjectDesc *obj_desc, con
*** 545,550 ****
--- 504,517 ----
errmsg("large object %u was not opened for writing",
obj_desc->id)));
+ /* check existence of the target largeobject */
+ if (!SearchSysCacheExists(LARGEOBJECTOID,
+ ObjectIdGetDatum(obj_desc->id),
+ 0, 0, 0))
+ 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 ****
--- 703,716 ----
errmsg("large object %u was not opened for writing",
obj_desc->id)));
+ /* check existence of the target largeobject */
+ if (!SearchSysCacheExists(LARGEOBJECTOID,
+ ObjectIdGetDatum(obj_desc->id),
+ 0, 0, 0))
+ 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 Thu Jul 30 21:01:28 2009
--- blob/src/backend/tcop/utility.c Fri Aug 28 16:20:01 2009
*************** CreateCommandTag(Node *parsetree)
*** 1602,1607 ****
--- 1602,1610 ----
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 Aug 20 22:07:11 2009
--- blob/src/backend/utils/adt/acl.c Sun Aug 30 00:43:51 2009
*************** acldefault(GrantObjectType objtype, Oid
*** 633,638 ****
--- 633,643 ----
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_ALL_RIGHTS_LARGEOBJECT;
+ 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/cache/syscache.c blob/src/backend/utils/cache/syscache.c
*** base/src/backend/utils/cache/syscache.c Thu Jun 18 10:20:52 2009
--- blob/src/backend/utils/cache/syscache.c Sun Aug 30 00:43:51 2009
***************
*** 35,40 ****
--- 35,41 ----
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
+ #include "catalog/pg_largeobject_meta.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
*************** static const struct cachedesc cacheinfo[
*** 452,457 ****
--- 453,470 ----
},
4
},
+ {LargeObjectMetaRelationId, /* LARGEOBJECTOID */
+ LargeObjectMetaOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
0,
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 Thu Aug 27 15:44:54 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 Sun Aug 30 00:43:51 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_meta_oid_index, 2337, on pg_largeobject_meta using btree(oid oid_ops));
+ #define LargeObjectMetaOidIndexId 2337
+
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_meta.h blob/src/include/catalog/pg_largeobject_meta.h
*** base/src/include/catalog/pg_largeobject_meta.h Thu Jan 1 09:00:00 1970
--- blob/src/include/catalog/pg_largeobject_meta.h Sun Aug 30 00:43:51 2009
***************
*** 0 ****
--- 1,66 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_largeobject_meta.h
+ * definition of the system "largeobject_meta" relation (pg_largeobject_meta)
+ * 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: pgsql/src/include/catalog/pg_largeobject_meta.h,v 1.24 2009/01/01 17:23:57 momjian Exp $
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_LARGEOBJECT_META_H
+ #define PG_LARGEOBJECT_META_H
+
+ #include "catalog/genbki.h"
+
+ /* ----------------
+ * pg_largeobject definition. cpp turns this into
+ * typedef struct FormData_pg_largeobject_meta
+ * ----------------
+ */
+ #define LargeObjectMetaRelationId 2336
+
+ CATALOG(pg_largeobject_meta,2336)
+ {
+ Oid lomowner; /* OID of the largeobject owner */
+ aclitem lomacl[1]; /* access permissions */
+ } FormData_pg_largeobject_meta;
+
+ /* ----------------
+ * Form_pg_largeobject corresponds to a pointer to a tuple with
+ * the format of pg_largeobject relation.
+ * ----------------
+ */
+ typedef FormData_pg_largeobject_meta *Form_pg_largeobject_meta;
+
+ /* ----------------
+ * compiler constants for pg_largeobject
+ * ----------------
+ */
+ #define Natts_pg_largeobject_meta 2
+ #define Anum_pg_largeobject_meta_lomowner 1
+ #define Anum_pg_largeobject_meta_lomacl 2
+
+ extern Oid CreateLargeObject(Oid loid);
+ extern void DropLargeObject(Oid loid);
+ extern void AlterLargeObjectOwner(Oid loid, Oid newOwnerId);
+
+ /* to be moved to backend/security/access_control.c */
+ extern void ac_largeobject_create(Oid loid);
+ extern void ac_largeobject_alter(Oid loid, Oid newOwner);
+ extern void ac_largeobject_drop(Oid loid, bool dacSkip);
+ extern void ac_largeobject_comment(Oid loid);
+ extern void ac_largeobject_read(Oid loid);
+ extern void ac_largeobject_write(Oid loid);
+ extern void ac_largeobject_export(Oid loid, const char *filename);
+ extern void ac_largeobject_import(Oid loid, const char *filename);
+
+ #endif /* PG_LARGEOBJECT_META_H */
diff -Nrpc base/src/include/nodes/parsenodes.h blob/src/include/nodes/parsenodes.h
*** base/src/include/nodes/parsenodes.h Mon Aug 3 17:23:24 2009
--- blob/src/include/nodes/parsenodes.h Wed Aug 26 23:14:55 2009
*************** typedef enum GrantObjectType
*** 1189,1194 ****
--- 1189,1195 ----
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 Thu Aug 27 11:03:48 2009
*************** typedef ArrayType Acl;
*** 151,156 ****
--- 151,157 ----
#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 ****
--- 182,188 ----
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 ****
--- 275,282 ----
AclMode mask, AclMaskHow how);
extern AclMode pg_language_aclmask(Oid lang_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+ extern AclMode pg_largeobject_aclmask(Oid lobj_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
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 ****
--- 294,300 ----
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(Oid lang_oid, Oid roleid, AclMode mode);
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 ****
--- 312,318 ----
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/include/utils/syscache.h blob/src/include/utils/syscache.h
*** base/src/include/utils/syscache.h Sat Jan 3 12:25:21 2009
--- blob/src/include/utils/syscache.h Wed Aug 26 17:43:12 2009
*************** enum SysCacheIdentifier
*** 58,63 ****
--- 58,64 ----
INDEXRELID,
LANGNAME,
LANGOID,
+ LARGEOBJECTOID,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff -Nrpc base/src/test/regress/expected/privileges.out blob/src/test/regress/expected/privileges.out
*** base/src/test/regress/expected/privileges.out Thu Aug 20 22:07:11 2009
--- blob/src/test/regress/expected/privileges.out Sun Aug 30 00:43:51 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_meta;
+ lo_unlink
+ -----------
+ (0 rows)
+
RESET client_min_messages;
-- test proper begins here
CREATE USER regressuser1;
*************** SELECT has_sequence_privilege('x_seq', '
*** 836,841 ****
--- 842,990 ----
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)
+
+ REVOKE ALL ON LARGE OBJECT 1002, 1003, 1004, 1005 FROM 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 largeobject
+ 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: largeobject 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
+ --------
+ \x
+ (1 row)
+
+ SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
+ ERROR: permission denied for largeobject 1002
+ SELECT loread(lo_open(1003, x'40000'::int), 32);
+ loread
+ --------
+ \x
+ (1 row)
+
+ SELECT loread(lo_open(1004, x'40000'::int), 32);
+ loread
+ --------
+ \x
+ (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 largeobject 1002
+ SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied
+ ERROR: permission denied for largeobject 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: largeobject 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 largeobject 1001
+ SELECT lo_unlink(2002);
+ lo_unlink
+ -----------
+ 1
+ (1 row)
+
+ \c -
+ -- confirm ACL setting
+ SELECT l.oid, a.rolname, l.lomacl FROM pg_largeobject_meta l JOIN pg_authid a ON l.lomowner = a.oid;
+ oid | rolname | lomacl
+ ------+--------------+------------------------------------------------------------------------------------------
+ 1001 | regressuser1 |
+ 1002 | regressuser1 | {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
+ ------------
+ \x61626364
+ (1 row)
+
+ SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied
+ ERROR: permission denied for largeobject 1003
+ SELECT loread(lo_open(1005, x'40000'::int), 32);
+ loread
+ --------
+ \x
+ (1 row)
+
+ SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied
+ ERROR: permission denied for largeobject 1005
+ SELECT lo_truncate(lo_open(2001, x'20000'::int), 10);
+ lo_truncate
+ -------------
+ 0
+ (1 row)
+
-- clean up
\c
drop sequence x_seq;
*************** DROP TABLE atest6;
*** 858,863 ****
--- 1007,1023 ----
DROP TABLE atestc;
DROP TABLE atestp1;
DROP TABLE atestp2;
+ SELECT lo_unlink(oid) FROM pg_largeobject_meta;
+ lo_unlink
+ -----------
+ 1
+ 1
+ 1
+ 1
+ 1
+ 1
+ (6 rows)
+
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
*************** DROP USER regressuser2;
*** 866,868 ****
--- 1026,1030 ----
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 Thu Jul 30 21:01:28 2009
--- blob/src/test/regress/expected/sanity_check.out Sun Aug 30 00:43:51 2009
*************** SELECT relname, relhasindex
*** 104,109 ****
--- 104,110 ----
pg_inherits | t
pg_language | t
pg_largeobject | t
+ pg_largeobject_meta | 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 Thu Aug 20 22:07:11 2009
--- blob/src/test/regress/sql/privileges.sql Sun Aug 30 00:43:51 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_meta;
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 ----
*************** SET SESSION AUTHORIZATION regressuser2;
*** 484,489 ****
--- 486,549 ----
SELECT has_sequence_privilege('x_seq', 'USAGE');
+ -- 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);
+
+ REVOKE ALL ON LARGE OBJECT 1002, 1003, 1004, 1005 FROM 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 l.oid, a.rolname, l.lomacl FROM pg_largeobject_meta l JOIN pg_authid a ON l.lomowner = a.oid;
+
+ 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);
+
-- clean up
\c
*************** DROP TABLE atestc;
*** 510,515 ****
--- 570,577 ----
DROP TABLE atestp1;
DROP TABLE atestp2;
+ SELECT lo_unlink(oid) FROM pg_largeobject_meta;
+
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
*************** DROP USER regressuser2;
*** 519,521 ****
--- 581,584 ----
DROP USER regressuser3;
DROP USER regressuser4;
DROP USER regressuser5;
+ DROP USER regressuser6;