diff -Nrpc base/doc/src/sgml/config.sgml blob/doc/src/sgml/config.sgml *** base/doc/src/sgml/config.sgml Tue Oct 6 08:45:40 2009 --- blob/doc/src/sgml/config.sgml Tue Oct 6 09:44:51 2009 *************** dynamic_library_path = 'C:\tools\postgre *** 4803,4808 **** --- 4803,4836 ---- + + largeobject_check_acl (boolean) + + + largeobject_check_acl configuration parameter + + + + + This allows us to turn on/off database ACL checks on largeobjects. + The PostgreSQL 8.4.x or prior releases + don't have any ACL checks on largeobjects. + So, turning the largeobject_check_acl off means + the largeobject feature performs in compatible mode. + + + Please note that it is not equivalent to disable all the security + checks corresponding to largeobjects. + For example, the lo_import() and + lo_export need superuser privileges independent + from this setting as prior versions were doing. + + + It is on by default. + + + + regex_flavor (enum) regular expressions diff -Nrpc base/doc/src/sgml/ref/allfiles.sgml blob/doc/src/sgml/ref/allfiles.sgml *** base/doc/src/sgml/ref/allfiles.sgml Tue Oct 6 08:45:40 2009 --- blob/doc/src/sgml/ref/allfiles.sgml Tue Oct 6 09:44:51 2009 *************** Complete list of usable sgml source file *** 16,21 **** --- 16,22 ---- + 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 Tue Oct 13 08:36:15 2009 --- blob/doc/src/sgml/ref/grant.sgml Tue Oct 13 09:28:23 2009 *************** GRANT { USAGE | ALL [ PRIVILEGES ] } *** 59,64 **** --- 59,68 ---- ON LANGUAGE lang_name [, ...] TO { [ GROUP ] role_name | 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 schema_name [, ...] TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ] *************** GRANT rol *** 170,175 **** --- 174,181 ---- . 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 *** 203,208 **** --- 209,216 ---- 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 Tue Oct 13 08:36:15 2009 --- blob/doc/src/sgml/ref/revoke.sgml Tue Oct 13 09:28:23 2009 *************** REVOKE [ GRANT OPTION FOR ] *** 76,81 **** --- 76,87 ---- [ 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 schema_name [, ...] FROM { [ GROUP ] role_name | PUBLIC } [, ...] diff -Nrpc base/doc/src/sgml/reference.sgml blob/doc/src/sgml/reference.sgml *** base/doc/src/sgml/reference.sgml Tue Oct 6 08:45:40 2009 --- blob/doc/src/sgml/reference.sgml Tue Oct 6 09:44:51 2009 *************** *** 44,49 **** --- 44,50 ---- &alterGroup; &alterIndex; &alterLanguage; + &alterLargeObject; &alterOperator; &alterOperatorClass; &alterOperatorFamily; diff -Nrpc base/src/backend/catalog/Makefile blob/src/backend/catalog/Makefile *** base/src/backend/catalog/Makefile Tue Oct 13 08:36:15 2009 --- blob/src/backend/catalog/Makefile Tue Oct 13 09:28:23 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_db_role_setting.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_db_role_setting.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 Tue Oct 13 08:36:15 2009 --- blob/src/backend/catalog/aclchk.c Tue Oct 13 09:28:23 2009 *************** *** 31,36 **** --- 31,37 ---- #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_language.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 *** 103,108 **** --- 104,110 ---- 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, *** 251,256 **** --- 253,261 ---- 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) *** 410,415 **** --- 415,424 ---- 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) *** 513,518 **** --- 522,530 ---- 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 *** 597,602 **** --- 609,628 ---- 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("largeobject %u does not exist", + lobjOid))); + + objects = lappend_oid(objects, lobjOid); + } + break; case ACL_OBJECT_NAMESPACE: foreach(cell, objnames) { *************** RemoveRoleFromObjectACL(Oid roleid, Oid *** 1279,1284 **** --- 1305,1313 ---- case LanguageRelationId: istmt.objtype = ACL_OBJECT_LANGUAGE; break; + case LargeObjectMetadataRelationId: + istmt.objtype = ACL_OBJECT_LARGEOBJECT; + break; case NamespaceRelationId: istmt.objtype = ACL_OBJECT_NAMESPACE; break; *************** ExecGrant_Language(InternalGrant *istmt) *** 2473,2478 **** --- 2502,2639 ---- } 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 largeobject %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), "largeobject %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(LargeObjectMetadataRelationId, + 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 *** 2812,2817 **** --- 2973,2980 ---- 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 *** 2850,2855 **** --- 3013,3020 ---- 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 *** 2969,2974 **** --- 3134,3141 ---- 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 *** 3352,3357 **** --- 3519,3596 ---- } /* + * 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; + 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, + SnapshotNow, 1, entry); + + tuple = systable_getnext(scan); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("largeobject %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 *** 3802,3807 **** --- 4041,4058 ---- } /* + * 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 *** 3992,3997 **** --- 4243,4291 ---- } /* + * Ownership check for a largeobject (specified by OID) + */ + 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_FUNCTION), + errmsg("largeobject %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 Oct 6 08:45:40 2009 --- blob/src/backend/catalog/dependency.c Tue Oct 6 09:44:51 2009 *************** *** 37,42 **** --- 37,43 ---- #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_language.h" + #include "catalog/pg_largeobject_metadata.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" *************** static const Oid object_classes[MAX_OCLA *** 131,136 **** --- 132,138 ---- ConversionRelationId, /* OCLASS_CONVERSION */ AttrDefaultRelationId, /* OCLASS_DEFAULT */ LanguageRelationId, /* OCLASS_LANGUAGE */ + LargeObjectMetadataRelationId, /* OCLASS_LARGEOBJECT */ OperatorRelationId, /* OCLASS_OPERATOR */ OperatorClassRelationId, /* OCLASS_OPCLASS */ OperatorFamilyRelationId, /* OCLASS_OPFAMILY */ *************** doDeletion(const ObjectAddress *object) *** 1074,1079 **** --- 1076,1085 ---- DropProceduralLanguageById(object->objectId); break; + case OCLASS_LARGEOBJECT: + DropLargeObject(object->objectId); + break; + case OCLASS_OPERATOR: RemoveOperatorById(object->objectId); break; *************** getObjectClass(const ObjectAddress *obje *** 1991,1996 **** --- 1997,2006 ---- Assert(object->objectSubId == 0); return OCLASS_LANGUAGE; + case LargeObjectMetadataRelationId: + Assert(object->objectSubId == 0); + return OCLASS_LARGEOBJECT; + case OperatorRelationId: Assert(object->objectSubId == 0); return OCLASS_OPERATOR; *************** getObjectDescription(const ObjectAddress *** 2243,2248 **** --- 2253,2262 ---- 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 Wed Oct 7 09:41:05 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/bytea.h" #include "utils/fmgroids.h" #include "utils/rel.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,445 ---- * 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_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; } void ! DropLargeObject(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); ! } ! ! void ! AlterLargeObjectOwner(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; + + /* 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_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(LargeObjectMetadataRelationId, + loid, newOwnerId); + } + systable_endscan(scan); + + heap_close(pg_lo_meta, RowExclusiveLock); } + /* + * In currently, we don't use system cache to keep metadata of + * the largeobject, because it may consume massive process local + * memory when a user tries to massive number of largeobjects + * concurrently. + * Use the LargeObjectExists() instead of SearchSysCacheExists(). + */ 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; } + + /* + * security check functions (to be moved to + * the backend/security/access_control.c) + */ + + /* + * ac_largeobject_check_acl + * + * It enables to turn on/off ACL checks on largeobjects to keep + * backward compatibility. The pgsql-8.4.x or prior didn't have + * any access controls on largeobjects (except for supruser checks + * on the server side import/export), so turning it off allows us + * to use the largeobject stuff as if older version doing. + */ + bool ac_largeobject_check_acl; + + /* + * ac_largeobject_create + * + * It checks permission to create a new largeobject. + * + * [Params] + * loid : InvalidOid or OID of the new largeobject if given + */ + 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. + */ + } + + /* + * ac_largeobject_alter + * + * It checks permission to alter a certain largeobject. + * (Now the only caller is ALTER LARGE OBJECT loid OWNER TO newowner) + * + * [Params] + * loid : OID of the largeobject to be altered + * newOwner : OID of the new largeobject owner + */ + void + ac_largeobject_alter(Oid loid, Oid newOwner) + { + /* + * MEMO: ac_largeobject_check_acl is not refered in this check, + * because we don't have ALTER LARGE OBJECT at the v8.4.x or + * prior release. + */ + + /* 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. + */ + } + } + + /* + * ac_largeobject_drop + * + * It checks permission to drop a certain largeobejct + * + * [Params] + * loid : OID of the largeobject to be altered + * cascade : True, if dac permission checks should be bypassed + */ + void ac_largeobject_drop(Oid loid, bool cascade) + { + /* Must be owner of the largeobject */ + if (!cascade && ac_largeobject_check_acl && + !pg_largeobject_ownercheck(loid, GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be owner of largeobject %u", loid))); + } + + /* + * ac_largeobject_comment + * + * It checks permission to comment on a certain largeobject + * + * [Params] + * loid : OID of the largeobject to be commented on + */ + void ac_largeobject_comment(Oid loid) + { + if (ac_largeobject_check_acl && + !pg_largeobject_ownercheck(loid, GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be owner of largeobject %u", loid))); + } + + /* + * ac_largeobject_read + * + * It checks permission to read data chunks from a certain largeobject + * + * [Params] + * loid : OID of the largeobject to be read from + */ + void ac_largeobject_read(Oid loid) + { + if (ac_largeobject_check_acl && + pg_largeobject_aclcheck(loid, GetUserId(), + ACL_SELECT) != ACLCHECK_OK) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for largeobject %u", loid))); + } + + /* + * ac_largeobject_write + * + * It checks permission to write data chunkd to a certain largeobject + * + * [Params] + * loid : OID of the largeobject to be written to + */ + void ac_largeobject_write(Oid loid) + { + if (ac_largeobject_check_acl && + pg_largeobject_aclcheck(loid, GetUserId(), + ACL_UPDATE) != ACLCHECK_OK) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for largeobject %u", loid))); + } + + /* + * ac_largeobject_export + * + * It checks permission to export a certain largeobject to a server-side file. + * + * [Params] + * loid : OID of the largeobject to be exported + * filename : The target filename to be exported to + */ + 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 + } + + /* + * ac_largeobject_import + * + * It checks permission to import contents from a server-side file. + * + * [Params] + * loid : InvalidOid or OID of the largeobject, if given + * filename : The target filename to be imported from + */ + 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 Tue Oct 13 08:36:15 2009 --- blob/src/backend/catalog/pg_shdepend.c Tue Oct 13 09:28:23 2009 *************** *** 25,30 **** --- 25,31 ---- #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" #include "catalog/pg_language.h" + #include "catalog/pg_largeobject_metadata.h" #include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" *************** shdepReassignOwned(List *roleids, Oid ne *** 1347,1352 **** --- 1348,1357 ---- AlterLanguageOwner_oid(sdepForm->objid, newrole); break; + case LargeObjectMetadataRelationId: + AlterLargeObjectOwner(sdepForm->objid, newrole); + break; + case DefaultAclRelationId: /* * Ignore default ACLs; they should be handled by 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 Thu Sep 24 10:46:38 2009 *************** *** 15,20 **** --- 15,21 ---- #include "postgres.h" #include "catalog/namespace.h" + #include "catalog/pg_largeobject_metadata.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 Tue Oct 13 08:36:15 2009 --- blob/src/backend/commands/comment.c Tue Oct 13 09:28:23 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_metadata.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" *************** CommentLargeObject(List *qualname, char *** 1435,1442 **** (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u does not exist", loid))); /* Call CreateComments() to create/drop the comments */ ! CreateComments(loid, LargeObjectRelationId, 0, comment); } /* --- 1435,1445 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u does not exist", loid))); + /* Permission checks */ + ac_largeobject_comment(loid); + /* Call CreateComments() to create/drop the comments */ ! CreateComments(loid, LargeObjectMetadataRelationId, 0, comment); } /* diff -Nrpc base/src/backend/commands/tablecmds.c blob/src/backend/commands/tablecmds.c *** base/src/backend/commands/tablecmds.c Tue Oct 13 08:36:15 2009 --- blob/src/backend/commands/tablecmds.c Tue Oct 13 09:28:23 2009 *************** ATExecAlterColumnType(AlteredTableInfo * *** 6143,6148 **** --- 6143,6149 ---- 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 Thu Sep 24 10:46:38 2009 *************** *** 42,47 **** --- 42,48 ---- #include #include + #include "catalog/pg_largeobject_metadata.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 Tue Oct 13 08:36:15 2009 --- blob/src/backend/parser/gram.y Tue Oct 13 09:32:15 2009 *************** static TypeName *TableFuncTypeName(List *** 398,403 **** --- 398,404 ---- %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: *** 4497,4502 **** --- 4498,4511 ---- n->objs = $2; $$ = n; } + | LARGE_P OBJECT_P Iconst_list + { + PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); + n->targtype = ACL_TARGET_OBJECT; + n->objtype = ACL_OBJECT_LARGEOBJECT; + n->objs = $3; + $$ = n; + } | SCHEMA name_list { PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); *************** AlterOwnerStmt: ALTER AGGREGATE func_nam *** 5772,5777 **** --- 5781,5794 ---- 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; } *** 10440,10445 **** --- 10457,10466 ---- | '-' 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 Wed Oct 7 09:59:59 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_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) *** 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) *** 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; } /* --- 160,183 ---- 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(LargeObjectMetadataRelationId, ! 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 **** --- 193,203 ---- { LargeObjectDesc *retval; + if (!LargeObjectExists(lobjId)) + 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; } --- 224,229 ---- *************** 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 --- 251,265 ---- int inv_drop(Oid lobjId) { ! ObjectAddress object; ! /* ! * Delete any comments and dependencies on the large object ! */ ! object.classId = LargeObjectMetadataRelationId; ! 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; --- 279,284 ---- *************** 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); --- 302,314 ---- * 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; } --- 323,332 ---- *************** inv_write(LargeObjectDesc *obj_desc, con *** 545,550 **** --- 503,514 ---- 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 **** --- 700,711 ---- 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 Oct 6 08:45:40 2009 --- blob/src/backend/tcop/utility.c Tue Oct 6 09:44:51 2009 *************** CreateCommandTag(Node *parsetree) *** 1611,1616 **** --- 1611,1619 ---- 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 Tue Oct 6 08:45:40 2009 --- blob/src/backend/utils/adt/acl.c Tue Oct 6 09:44:51 2009 *************** acldefault(GrantObjectType objtype, Oid *** 763,768 **** --- 763,773 ---- 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 Tue Oct 6 08:45:40 2009 --- blob/src/backend/utils/misc/guc.c Tue Oct 6 09:44:51 2009 *************** *** 31,36 **** --- 31,37 ---- #include "access/twophase.h" #include "access/xact.h" #include "catalog/namespace.h" + #include "catalog/pg_largeobject_metadata.h" #include "commands/async.h" #include "commands/prepare.h" #include "commands/vacuum.h" *************** static struct config_bool ConfigureNames *** 1239,1244 **** --- 1240,1254 ---- false, NULL, NULL }, + { + {"largeobject_check_acl", PGC_SUSET, COMPAT_OPTIONS, + gettext_noop("Enables/Disables permission check for largeobjects."), + NULL, + }, + &ac_largeobject_check_acl, + true, 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 Sep 24 08:43:31 2009 --- blob/src/backend/utils/misc/postgresql.conf.sample Fri Sep 25 09:00:55 2009 *************** *** 489,494 **** --- 489,495 ---- #backslash_quote = safe_encoding # on, off, or safe_encoding #default_with_oids = off #escape_string_warning = on + #largeobject_check_acl = on #regex_flavor = advanced # advanced, extended, or basic #sql_inheritance = on #standard_conforming_strings = off 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 Thu Sep 24 11:34:54 2009 *************** do_lo_list(void) *** 279,289 **** 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); --- 279,291 ---- printQueryOpt myopt = pset.popt; 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_metadata') as \"%s\"\n" ! " FROM pg_largeobject_metadata " ! " ORDER BY oid", gettext_noop("ID"), + gettext_noop("Owner"), gettext_noop("Description")); res = PSQLexec(buf, false); diff -Nrpc base/src/include/catalog/dependency.h blob/src/include/catalog/dependency.h *** base/src/include/catalog/dependency.h Tue Oct 13 08:36:15 2009 --- blob/src/include/catalog/dependency.h Tue Oct 13 09:28:23 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 Tue Oct 13 08:36:15 2009 --- blob/src/include/catalog/indexing.h Tue Oct 13 09:28:23 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, 2337, on pg_largeobject_metadata using btree(oid oid_ops)); + #define LargeObjectMetadataOidIndexId 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.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 Wed Oct 7 09:41:05 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,54 ---- 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 Wed Oct 7 09:41:05 2009 *************** *** 0 **** --- 1,68 ---- + /*------------------------------------------------------------------------- + * + * 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: pgsql/src/include/catalog/pg_largeobject_metadata.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_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 2336 + + CATALOG(pg_largeobject_metadata,2336) + { + 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 + + extern Oid CreateLargeObject(Oid loid); + extern void DropLargeObject(Oid loid); + extern void AlterLargeObjectOwner(Oid loid, Oid newOwnerId); + extern bool LargeObjectExists(Oid loid); + + /* to be moved to backend/security/access_control.c */ + extern bool ac_largeobject_check_acl; + 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_METADATA_H */ diff -Nrpc base/src/include/nodes/parsenodes.h blob/src/include/nodes/parsenodes.h *** base/src/include/nodes/parsenodes.h Tue Oct 13 08:36:15 2009 --- blob/src/include/nodes/parsenodes.h Tue Oct 13 09:28:23 2009 *************** typedef enum GrantObjectType *** 1199,1204 **** --- 1199,1205 ---- 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 Tue Oct 6 08:45:40 2009 --- blob/src/include/utils/acl.h Tue Oct 6 09:44:51 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_ *** 258,263 **** --- 260,267 ---- 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 *** 275,280 **** --- 279,285 ---- 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_ *** 292,297 **** --- 297,303 ---- 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 Tue Oct 13 08:36:15 2009 --- blob/src/test/regress/expected/privileges.out Tue Oct 13 09:28:23 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_sequence_privilege('x_seq', ' *** 836,841 **** --- 842,1035 ---- 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 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 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 + ------------ + \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) + + -- compatibility mode in largeobject permission + \c - + SET largeobject_check_acl = true; -- default setting + SET SESSION AUTHORIZATION regressuser4; + SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied + ERROR: permission denied for largeobject 1002 + SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied + ERROR: permission denied for largeobject 1002 + SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied + ERROR: permission denied for largeobject 1002 + SELECT lo_unlink(1002); -- to be denied + ERROR: must be owner of largeobject 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 largeobject_check_acl = false; -- compatibility mode + SET SESSION AUTHORIZATION regressuser4; + SELECT loread(lo_open(1002, x'40000'::int), 32); + loread + -------- + \x + (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. -- test default ACLs \c - CREATE SCHEMA testns; *************** DROP TABLE atest6; *** 1023,1028 **** --- 1217,1232 ---- 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; -- these are needed to clean up permissions *************** DROP USER regressuser2; *** 1033,1035 **** --- 1237,1241 ---- 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 Oct 13 08:36:15 2009 --- blob/src/test/regress/expected/sanity_check.out Tue Oct 13 10:17:59 2009 *************** SELECT relname, relhasindex *** 106,111 **** --- 106,112 ---- 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 *** 153,159 **** timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (142 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 154,160 ---- timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (143 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 Tue Oct 13 08:36:15 2009 --- blob/src/test/regress/sql/privileges.sql Tue Oct 13 09:28:23 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 ---- *************** SET SESSION AUTHORIZATION regressuser2; *** 484,489 **** --- 486,568 ---- 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); + + 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 largeobject_check_acl = true; -- 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 largeobject_check_acl = false; -- 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 -- test default ACLs \c - *************** DROP TABLE atestc; *** 610,615 **** --- 689,696 ---- DROP TABLE atestp1; DROP TABLE atestp2; + SELECT lo_unlink(oid) FROM pg_largeobject_metadata; + DROP GROUP regressgroup1; DROP GROUP regressgroup2; *************** DROP USER regressuser2; *** 622,624 **** --- 703,706 ---- DROP USER regressuser3; DROP USER regressuser4; DROP USER regressuser5; + DROP USER regressuser6;