-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.212 2009/12/07 05:22:21 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.213 2009/12/11 03:34:54 itagaki Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
<row>
<entry><link linkend="catalog-pg-largeobject"><structname>pg_largeobject</structname></link></entry>
- <entry>large objects</entry>
+ <entry>data pages for large objects</entry>
+ </row>
+
+ <row>
+ <entry><link linkend="catalog-pg-largeobject-metadata"><structname>pg_largeobject_metadata</structname></link></entry>
+ <entry>metadata for large objects</entry>
</row>
<row>
<para>
The catalog <structname>pg_largeobject</structname> holds the data making up
- <quote>large objects</quote>. A large object is identified by an
- OID assigned when it is created. Each large object is broken into
+ <quote>large objects</quote>. A large object is identified by an OID of
+ <link linkend="catalog-pg-largeobject-metadata"><structname>pg_largeobject_metadata</></link>
+ catalog, assigned when it is created. Each large object is broken into
segments or <quote>pages</> small enough to be conveniently stored as rows
in <structname>pg_largeobject</structname>.
The amount of data per page is defined to be <symbol>LOBLKSIZE</> (which is currently
<literal>BLCKSZ/4</>, or typically 2 kB).
</para>
+ <para>
+ <structname>pg_largeobject</structname> should not be readable by the
+ public, since the catalog contains data in large objects of all users.
+ <structname>pg_largeobject_metadata</> is a publicly readable catalog
+ that only contains identifiers of large objects.
+ </para>
+
<table>
<title><structname>pg_largeobject</> Columns</title>
- <tgroup cols="3">
+ <tgroup cols="4">
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
+ <entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<row>
<entry><structfield>loid</structfield></entry>
<entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-largeobject-metadata"><structname>pg_largeobject_metadata</structname></link>.oid</literal></entry>
<entry>Identifier of the large object that includes this page</entry>
</row>
<row>
<entry><structfield>pageno</structfield></entry>
<entry><type>int4</type></entry>
+ <entry></entry>
<entry>Page number of this page within its large object
(counting from zero)</entry>
</row>
<row>
<entry><structfield>data</structfield></entry>
<entry><type>bytea</type></entry>
+ <entry></entry>
<entry>
Actual data stored in the large object.
This will never be more than <symbol>LOBLKSIZE</> bytes and might be less
</sect1>
+ <sect1 id="catalog-pg-largeobject-metadata">
+ <title><structname>pg_largeobject_metadata</structname></title>
+
+ <indexterm zone="catalog-pg-largeobject-metadata">
+ <primary>pg_largeobject_metadata</primary>
+ </indexterm>
+
+ <para>
+ The purpose of <structname>pg_largeobject_metadata</structname> is to
+ hold metadata of <quote>large objects</quote>, such as OID of its owner,
+ access permissions and OID of the large object itself.
+ </para>
+
+ <table>
+ <title><structname>pg_largeobject_metadata</> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>lomowner</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+ <entry>Owner of the largeobejct</entry>
+ </row>
+
+ <row>
+ <entry><structfield>lomacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry>
+ Access privileges; see
+ <xref linkend="sql-grant" endterm="sql-grant-title"> and
+ <xref linkend="sql-revoke" endterm="sql-revoke-title">
+ for details
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
<sect1 id="catalog-pg-listener">
<title><structname>pg_listener</structname></title>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.236 2009/12/10 06:32:27 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.237 2009/12/11 03:34:55 itagaki Exp $ -->
<chapter Id="runtime-config">
<title>Server Configuration</title>
</listitem>
</varlistentry>
+ <varlistentry id="guc-lo-compat-privileges" xreflabel="lo_compat_privileges">
+ <term><varname>lo_compat_privileges</varname> (<type>boolean</type>)</term>
+ <indexterm>
+ <primary>
+ <varname>lo_compat_privileges</varname> configuration parameter
+ </primary>
+ </indexterm>
+ <listitem>
+ <para>
+ This allows us to tuen on/off database privilege checks on large
+ objects. In the 8.4.x series and earlier release do not have
+ privilege checks on large object in most cases.
+
+ So, turning the <varname>lo_compat_privileges</varname> off means
+ the large object feature performs in compatible mode.
+ </para>
+ <para>
+ Please note that it is not equivalent to disable all the security
+ checks corresponding to large objects.
+ For example, the <literal>lo_import()</literal> and
+ <literal>lo_export()</literal> need superuser privileges independent
+ from this setting as prior versions were doing.
+ </para>
+ <para>
+ It is <literal>off</literal> by default.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-sql-inheritance" xreflabel="sql_inheritance">
<term><varname>sql_inheritance</varname> (<type>boolean</type>)</term>
<indexterm>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.49 2008/12/07 23:46:39 alvherre Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/lobj.sgml,v 1.50 2009/12/11 03:34:55 itagaki Exp $ -->
<chapter id="largeObjects">
<title id="largeObjects-title">Large Objects</title>
The client-side functions can be used by any
<productname>PostgreSQL</productname> user.
</para>
+
+ <sect2 id="lo-func-privilege">
+ <title>Large object and privileges</title>
+ <para>
+ Note that access control feature was not supported in the 8.4.x series
+ and earlier release.
+ Also see the <xref linkend="guc-lo-compat-privileges"> compatibility
+ option.
+ </para>
+ <para>
+ Now it supports access controls on large objects, and allows the owner
+ of large objects to set up access rights using
+ <xref linkend="sql-grant" endterm="sql-grant-title"> and
+ <xref linkend="sql-revoke" endterm="sql-revoke-title"> statement.
+ </para>
+ <para>
+ Two permissions are defined on the large object class.
+ These are checked only when <xref linkend="guc-lo-compat-privileges">
+ option is disabled.
+ </para>
+ <para>
+ The first is <literal>SELECT</literal>.
+ It is required on <function>loread()</function> function.
+ Note that when we open large object with read-only mode, we can see
+ a static image even if other concurrent transaction modified the
+ same large object.
+ This principle is also applied on the access rights of large objects.
+ Even if a transaction modified access rights and commit it, it is
+ not invisible from other transaction which already opened the large
+ object.
+ </para>
+ <para>
+ The second is <literal>UPDATE</literal>.
+ It is required on <function>lowrite()</function> function and
+ <function>lo_truncate()</function> function.
+ </para>
+ <para>
+ In addition, <function>lo_unlink()</function> function,
+ <command>COMMENT ON</command> and <command>ALTER LARGE OBJECT</command>
+ statements needs ownership of the large object to be accessed.
+ </para>
+ <para>
+ You may wonder why <literal>SELECT</literal> is not checked on the
+ <function>lo_export()</function> function or <literal>UPDATE</literal>
+ is not checked on the <function>lo_import</function> function.
+
+ These functions originally require database superuser privilege,
+ and it allows to bypass the default database privilege checks,
+ so we don't need to check an obvious test twice.
+ </para>
+ </sect2>
</sect1>
<sect1 id="lo-examplesect">
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.76 2009/10/05 19:24:33 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.77 2009/12/11 03:34:55 itagaki Exp $
PostgreSQL documentation
Complete list of usable sgml source files in this directory.
-->
<!entity alterGroup system "alter_group.sgml">
<!entity alterIndex system "alter_index.sgml">
<!entity alterLanguage system "alter_language.sgml">
+<!entity alterLargeObject system "alter_large_object.sgml">
<!entity alterOperator system "alter_operator.sgml">
<!entity alterOperatorClass system "alter_opclass.sgml">
<!entity alterOperatorFamily system "alter_opfamily.sgml">
--- /dev/null
+<refentry id="SQL-ALTERLARGEOBJECT">
+ <refmeta>
+ <refentrytitle id="SQL-ALTERLARGEOBJECT-title">ALTER LARGE OBJECT</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER LARGE OBJECT</refname>
+ <refpurpose>change the definition of a large object</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterlargeobject">
+ <primary>ALTER LARGE OBJECT</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER LARGE OBJECT <replaceable class="PARAMETER">large_object_oid</replaceable> OWNER TO <replaceable>new_owner</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>ALTER LARGE OBJECT</command> changes the definition of a
+ large object. The only functionality is to assign a new owner.
+ You must be superuser or owner of the large object to use
+ <command>ALTER LARGE OBJECT</command>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><replaceable>large_object_oid</replaceable></term>
+ <listitem>
+ <para>
+ OID of the large object to be altered
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable>new_owner</replaceable></term>
+ <listitem>
+ <para>
+ The new owner of the large object
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ There is no <command>ALTER LARGE OBJECT</command> statement in the SQL
+ standard.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="largeObjects" endterm="largeObjects-title"></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.79 2009/10/12 20:39:39 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.80 2009/12/11 03:34:55 itagaki Exp $
PostgreSQL documentation
-->
ON LANGUAGE <replaceable>lang_name</replaceable> [, ...]
TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+GRANT { { SELECT | UPDATE } [,...] | ALL [ PRIVILEGES ] }
+ ON LARGE OBJECT <replaceable class="PARAMETER">loid</replaceable> [, ...]
+ TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
ON SCHEMA <replaceable>schema_name</replaceable> [, ...]
TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
<xref linkend="sql-delete" endterm="sql-delete-title">.
For sequences, this privilege also allows the use of the
<function>currval</function> function.
+ For large objects, this privilege also allows to read from
+ the target large object.
</para>
</listitem>
</varlistentry>
<literal>SELECT</literal> privilege. For sequences, this
privilege allows the use of the <function>nextval</function> and
<function>setval</function> functions.
+ For large objects, this privilege also allows to write or truncate
+ on the target large object.
</para>
</listitem>
</varlistentry>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.53 2009/10/12 20:39:39 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.54 2009/12/11 03:34:55 itagaki Exp $
PostgreSQL documentation
-->
FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
[ CASCADE | RESTRICT ]
+REVOKE [ GRANT OPTION FOR ]
+ { { SELECT | UPDATE } [,...] | ALL [ PRIVILEGES ] }
+ ON LARGE OBJECT <replaceable class="PARAMETER">loid</replaceable> [, ...]
+ FROM { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...]
+ [ CASCADE | RESTRICT ]
+
REVOKE [ GRANT OPTION FOR ]
{ { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
ON SCHEMA <replaceable>schema_name</replaceable> [, ...]
-<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.69 2009/10/05 19:24:33 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.70 2009/12/11 03:34:55 itagaki Exp $ -->
<part id="reference">
<title>Reference</title>
&alterGroup;
&alterIndex;
&alterLanguage;
+ &alterLargeObject;
&alterOperator;
&alterOperatorClass;
&alterOperatorFamily;
#
# Makefile for backend/catalog
#
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.73 2009/10/07 22:14:16 alvherre Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.74 2009/12/11 03:34:55 itagaki Exp $
#
#-------------------------------------------------------------------------
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_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 \
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.156 2009/10/12 20:39:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.157 2009/12/11 03:34:55 itagaki Exp $
*
* NOTES
* See acl.h.
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
+#include "catalog/pg_largeobject.h"
+#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
static void ExecGrant_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);
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;
all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
errormsg = gettext_noop("invalid privilege type %s for language");
break;
+ case ACL_OBJECT_LARGEOBJECT:
+ all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
+ errormsg = gettext_noop("invalid privilege type %s for large object");
+ break;
case ACL_OBJECT_NAMESPACE:
all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
errormsg = gettext_noop("invalid privilege type %s for schema");
case ACL_OBJECT_LANGUAGE:
ExecGrant_Language(istmt);
break;
+ case ACL_OBJECT_LARGEOBJECT:
+ ExecGrant_Largeobject(istmt);
+ break;
case ACL_OBJECT_NAMESPACE:
ExecGrant_Namespace(istmt);
break;
ReleaseSysCache(tuple);
}
break;
+ case ACL_OBJECT_LARGEOBJECT:
+ foreach(cell, objnames)
+ {
+ Oid lobjOid = intVal(lfirst(cell));
+
+ if (!LargeObjectExists(lobjOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist",
+ lobjOid)));
+
+ objects = lappend_oid(objects, lobjOid);
+ }
+ break;
case ACL_OBJECT_NAMESPACE:
foreach(cell, objnames)
{
case LanguageRelationId:
istmt.objtype = ACL_OBJECT_LANGUAGE;
break;
+ case LargeObjectRelationId:
+ istmt.objtype = ACL_OBJECT_LARGEOBJECT;
+ break;
case NamespaceRelationId:
istmt.objtype = ACL_OBJECT_NAMESPACE;
break;
heap_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Largeobject(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
+
+ relation = heap_open(LargeObjectMetadataRelationId,
+ RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid loid = lfirst_oid(cell);
+ Form_pg_largeobject_metadata form_lo_meta;
+ char loname[NAMEDATALEN];
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_largeobject_metadata];
+ bool nulls[Natts_pg_largeobject_metadata];
+ bool replaces[Natts_pg_largeobject_metadata];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+ ScanKeyData entry[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+
+ /* There's no syscache for pg_largeobject_metadata */
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+
+ scan = systable_beginscan(relation,
+ LargeObjectMetadataOidIndexId, true,
+ SnapshotNow, 1, entry);
+
+ tuple = systable_getnext(scan);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for large object %u", loid);
+
+ form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = form_lo_meta->lomowner;
+ aclDatum = heap_getattr(tuple,
+ Anum_pg_largeobject_metadata_lomacl,
+ RelationGetDescr(relation), &isNull);
+ if (isNull)
+ old_acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId);
+ else
+ old_acl = DatumGetAclPCopy(aclDatum);
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, ownerId,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ snprintf(loname, sizeof(loname), "large object %u", loid);
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ loid, grantorId, ACL_KIND_LARGEOBJECT,
+ loname, 0, NULL);
+
+ /*
+ * Generate new ACL.
+ *
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, ownerId);
+
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
+ values[Anum_pg_largeobject_metadata_lomacl - 1]
+ = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
+ values, nulls, replaces);
+
+ simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, newtuple);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(LargeObjectRelationId,
+ HeapTupleGetOid(tuple), 0,
+ ownerId, istmt->is_grant,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ systable_endscan(scan);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ heap_close(relation, RowExclusiveLock);
+}
+
static void
ExecGrant_Namespace(InternalGrant *istmt)
{
gettext_noop("permission denied for type %s"),
/* ACL_KIND_LANGUAGE */
gettext_noop("permission denied for language %s"),
+ /* ACL_KIND_LARGEOBJECT */
+ gettext_noop("permission denied for large object %s"),
/* ACL_KIND_NAMESPACE */
gettext_noop("permission denied for schema %s"),
/* ACL_KIND_OPCLASS */
gettext_noop("must be owner of type %s"),
/* ACL_KIND_LANGUAGE */
gettext_noop("must be owner of language %s"),
+ /* ACL_KIND_LARGEOBJECT */
+ gettext_noop("must be owner of large object %s"),
/* ACL_KIND_NAMESPACE */
gettext_noop("must be owner of schema %s"),
/* ACL_KIND_OPCLASS */
return pg_proc_aclmask(table_oid, roleid, mask, how);
case ACL_KIND_LANGUAGE:
return pg_language_aclmask(table_oid, roleid, mask, how);
+ case ACL_KIND_LARGEOBJECT:
+ return pg_largeobject_aclmask_snapshot(table_oid, roleid,
+ mask, how, SnapshotNow);
case ACL_KIND_NAMESPACE:
return pg_namespace_aclmask(table_oid, roleid, mask, how);
case ACL_KIND_TABLESPACE:
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a largeobject
+ *
+ * The reason why this interface has an argument of snapshot is that
+ * we apply a snapshot available on lo_open(), not SnapshotNow, when
+ * it is opened as read-only mode.
+ * If we could see the metadata and data from inconsistent viewpoint,
+ * it will give us much confusion. So, we need to provide an interface
+ * which takes an argument of snapshot.
+ *
+ * If the caller refers a large object with a certain snapshot except
+ * for SnapshotNow, its permission checks should be also applied in
+ * the same snapshot.
+ */
+AclMode
+pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
+ AclMode mask, AclMaskHow how,
+ Snapshot snapshot)
+{
+ AclMode result;
+ Relation pg_lo_meta;
+ ScanKeyData entry[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Get the largeobject's ACL from pg_language_metadata
+ */
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(lobj_oid));
+
+ scan = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
+ snapshot, 1, entry);
+
+ tuple = systable_getnext(scan);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist", lobj_oid)));
+
+ ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
+
+ aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl,
+ RelationGetDescr(pg_lo_meta), &isNull);
+
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ systable_endscan(scan);
+
+ heap_close(pg_lo_meta, AccessShareLock);
+
+ return result;
+}
+
/*
* Exported routine for examining a user's privileges for a namespace
*/
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a largeobject
+ */
+AclResult
+pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode,
+ Snapshot snapshot)
+{
+ if (pg_largeobject_aclmask_snapshot(lobj_oid, roleid, mode,
+ ACLMASK_ANY, snapshot) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Exported routine for checking a user's access privileges to a namespace
*/
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a largeobject (specified by OID)
+ *
+ * Note that we have no candidate to call this routine with a certain
+ * snapshot except for SnapshotNow, so we don't provide an interface
+ * with _snapshot() version now.
+ */
+bool
+pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid)
+{
+ Relation pg_lo_meta;
+ ScanKeyData entry[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ /* There's no syscache for pg_largeobject_metadata */
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(lobj_oid));
+
+ scan = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
+ SnapshotNow, 1, entry);
+
+ tuple = systable_getnext(scan);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist", lobj_oid)));
+
+ ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
+
+ systable_endscan(scan);
+ heap_close(pg_lo_meta, AccessShareLock);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
/*
* Ownership check for a namespace (specified by OID).
*/
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.92 2009/10/05 19:24:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.93 2009/12/11 03:34:55 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
+#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
LanguageRelationId, /* OCLASS_LANGUAGE */
+ LargeObjectRelationId, /* OCLASS_LARGEOBJECT */
OperatorRelationId, /* OCLASS_OPERATOR */
OperatorClassRelationId, /* OCLASS_OPCLASS */
OperatorFamilyRelationId, /* OCLASS_OPFAMILY */
DropProceduralLanguageById(object->objectId);
break;
+ case OCLASS_LARGEOBJECT:
+ LargeObjectDrop(object->objectId);
+ break;
+
case OCLASS_OPERATOR:
RemoveOperatorById(object->objectId);
break;
Assert(object->objectSubId == 0);
return OCLASS_LANGUAGE;
+ case LargeObjectRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_LARGEOBJECT;
+
case OperatorRelationId:
Assert(object->objectSubId == 0);
return OCLASS_OPERATOR;
ReleaseSysCache(langTup);
break;
}
+ case OCLASS_LARGEOBJECT:
+ appendStringInfo(&buffer, _("large object %u"),
+ object->objectId);
+ break;
case OCLASS_OPERATOR:
appendStringInfo(&buffer, _("operator %s"),
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_largeobject.c,v 1.33 2009/08/04 16:08:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_largeobject.c,v 1.34 2009/12/11 03:34:55 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
#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"
/*
* Create a large object having the given LO identifier.
*
- * We do this by inserting an empty first page, so that the object will
- * appear to exist with size 0. Note that the unique index will reject
- * an attempt to create a duplicate page.
+ * We create a new large object by inserting an entry into
+ * pg_largeobject_metadata without any data pages, so that the object
+ * will appear to exist with size 0.
*/
-void
+Oid
LargeObjectCreate(Oid loid)
{
- Relation pg_largeobject;
+ Relation pg_lo_meta;
HeapTuple ntup;
- Datum values[Natts_pg_largeobject];
- bool nulls[Natts_pg_largeobject];
- int i;
+ Oid loid_new;
+ Datum values[Natts_pg_largeobject_metadata];
+ bool nulls[Natts_pg_largeobject_metadata];
- pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock);
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ RowExclusiveLock);
/*
- * Form new tuple
+ * Insert metadata of the largeobject
*/
- for (i = 0; i < Natts_pg_largeobject; i++)
- {
- values[i] = (Datum) NULL;
- nulls[i] = false;
- }
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
- i = 0;
- values[i++] = ObjectIdGetDatum(loid);
- values[i++] = Int32GetDatum(0);
- values[i++] = DirectFunctionCall1(byteain,
- CStringGetDatum(""));
+ values[Anum_pg_largeobject_metadata_lomowner - 1]
+ = ObjectIdGetDatum(GetUserId());
+ nulls[Anum_pg_largeobject_metadata_lomacl - 1] = true;
- ntup = heap_form_tuple(pg_largeobject->rd_att, values, nulls);
+ ntup = heap_form_tuple(RelationGetDescr(pg_lo_meta),
+ values, nulls);
+ if (OidIsValid(loid))
+ HeapTupleSetOid(ntup, loid);
- /*
- * Insert it
- */
- simple_heap_insert(pg_largeobject, ntup);
-
- /* Update indexes */
- CatalogUpdateIndexes(pg_largeobject, ntup);
+ loid_new = simple_heap_insert(pg_lo_meta, ntup);
+ Assert(!OidIsValid(loid) || loid == loid_new);
- heap_close(pg_largeobject, RowExclusiveLock);
+ CatalogUpdateIndexes(pg_lo_meta, ntup);
heap_freetuple(ntup);
+
+ heap_close(pg_lo_meta, RowExclusiveLock);
+
+ return loid_new;
}
+/*
+ * Drop a large object having the given LO identifier.
+ *
+ * When we drop a large object, it is necessary to drop both of metadata
+ * and data pages in same time.
+ */
void
LargeObjectDrop(Oid loid)
{
- bool found = false;
+ Relation pg_lo_meta;
Relation pg_largeobject;
ScanKeyData skey[1];
- SysScanDesc sd;
+ 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],
- Anum_pg_largeobject_loid,
+ ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(loid));
+ ObjectIdGetDatum(loid));
- pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock);
+ scan = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
+ SnapshotNow, 1, skey);
- sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, 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));
- while ((tuple = systable_getnext(sd)) != NULL)
+ scan = systable_beginscan(pg_largeobject,
+ LargeObjectLOidPNIndexId, true,
+ SnapshotNow, 1, skey);
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
simple_heap_delete(pg_largeobject, &tuple->t_self);
- found = true;
}
- systable_endscan(sd);
+ systable_endscan(scan);
heap_close(pg_largeobject, RowExclusiveLock);
- if (!found)
+ heap_close(pg_lo_meta, RowExclusiveLock);
+}
+
+/*
+ * LargeObjectAlterOwner
+ *
+ * Implementation of ALTER LARGE OBJECT statement
+ */
+void
+LargeObjectAlterOwner(Oid loid, Oid newOwnerId)
+{
+ Form_pg_largeobject_metadata form_lo_meta;
+ Relation pg_lo_meta;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
+ HeapTuple oldtup;
+ HeapTuple newtup;
+
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ RowExclusiveLock);
+
+ ScanKeyInit(&skey[0],
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+
+ scan = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ oldtup = systable_getnext(scan);
+ if (!HeapTupleIsValid(oldtup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", loid)));
+
+ form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(oldtup);
+ if (form_lo_meta->lomowner != newOwnerId)
+ {
+ Datum values[Natts_pg_largeobject_metadata];
+ bool nulls[Natts_pg_largeobject_metadata];
+ bool replaces[Natts_pg_largeobject_metadata];
+ Acl *newAcl;
+ Datum aclDatum;
+ bool isnull;
+
+ /* Superusers can always do it */
+ if (!superuser())
+ {
+ /*
+ * The 'lo_compat_privileges' is not checked here, because we
+ * don't have any access control features in the 8.4.x series
+ * or earlier release.
+ * So, it is not a place we can define a compatible behavior.
+ */
+
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_largeobject_ownercheck(loid, GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of large object %u", loid)));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+ }
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replaces, false, sizeof(nulls));
+
+ values[Anum_pg_largeobject_metadata_lomowner - 1]
+ = ObjectIdGetDatum(newOwnerId);
+ replaces[Anum_pg_largeobject_metadata_lomowner - 1] = true;
+
+ /*
+ * Determine the modified ACL for the new owner.
+ * This is only necessary when the ACL is non-null.
+ */
+ aclDatum = heap_getattr(oldtup,
+ Anum_pg_largeobject_metadata_lomacl,
+ RelationGetDescr(pg_lo_meta), &isnull);
+ if (!isnull)
+ {
+ newAcl = aclnewowner(DatumGetAclP(aclDatum),
+ form_lo_meta->lomowner, newOwnerId);
+ values[Anum_pg_largeobject_metadata_lomacl - 1]
+ = PointerGetDatum(newAcl);
+ replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
+ }
+
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_lo_meta),
+ values, nulls, replaces);
+
+ simple_heap_update(pg_lo_meta, &newtup->t_self, newtup);
+ CatalogUpdateIndexes(pg_lo_meta, newtup);
+
+ heap_freetuple(newtup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(LargeObjectRelationId,
+ loid, newOwnerId);
+ }
+ systable_endscan(scan);
+
+ heap_close(pg_lo_meta, RowExclusiveLock);
}
+/*
+ * LargeObjectExists
+ *
+ * Currently, we don't use system cache to contain metadata of
+ * large objects, because massive number of large objects can
+ * consume not a small amount of process local memory.
+ *
+ * Note that LargeObjectExists always scans the system catalog
+ * with SnapshotNow, so it is unavailable to use to check
+ * existence in read-only accesses.
+ */
bool
LargeObjectExists(Oid loid)
{
+ Relation pg_lo_meta;
+ ScanKeyData skey[1];
+ SysScanDesc sd;
+ HeapTuple tuple;
bool retval = false;
- 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,
+ ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(loid));
- pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock);
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ AccessShareLock);
- sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
+ sd = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
SnapshotNow, 1, skey);
- if (systable_getnext(sd) != NULL)
+ tuple = systable_getnext(sd);
+ if (HeapTupleIsValid(tuple))
retval = true;
systable_endscan(sd);
- heap_close(pg_largeobject, AccessShareLock);
+ heap_close(pg_lo_meta, AccessShareLock);
return retval;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.36 2009/10/07 22:14:18 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.37 2009/12/11 03:34:55 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_language.h"
+#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
AlterLanguageOwner_oid(sdepForm->objid, newrole);
break;
+ case LargeObjectRelationId:
+ LargeObjectAlterOwner(sdepForm->objid, newrole);
+ break;
+
case DefaultAclRelationId:
/*
* Ignore default ACLs; they should be handled by
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.31 2009/01/01 17:23:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.32 2009/12/11 03:34:55 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/namespace.h"
+#include "catalog/pg_largeobject.h"
#include "commands/alter.h"
#include "commands/conversioncmds.h"
#include "commands/dbcommands.h"
AlterLanguageOwner(strVal(linitial(stmt->object)), newowner);
break;
+ case OBJECT_LARGEOBJECT:
+ LargeObjectAlterOwner(intVal(linitial(stmt->object)), newowner);
+ break;
+
case OBJECT_OPERATOR:
Assert(list_length(stmt->objarg) == 2);
AlterOperatorOwner(stmt->object,
* Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.108 2009/10/12 19:49:24 adunstan Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.109 2009/12/11 03:34:55 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/pg_description.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
+#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/tablespace.h"
+#include "libpq/be-fsstubs.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", loid)));
- /* Call CreateComments() to create/drop the comments */
+ /* Permission checks */
+ if (!lo_compat_privileges &&
+ !pg_largeobject_ownercheck(loid, GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of large object %u", loid)));
+
+ /*
+ * Call CreateComments() to create/drop the comments
+ *
+ * See the comment in the inv_create() which describes
+ * the reason why LargeObjectRelationId is used instead
+ * of the LargeObjectMetadataRelationId.
+ */
CreateComments(loid, LargeObjectRelationId, 0, comment);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.308 2009/12/09 21:57:50 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.309 2009/12/11 03:34:55 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
case OCLASS_CAST:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
+ case OCLASS_LARGEOBJECT:
case OCLASS_OPERATOR:
case OCLASS_OPCLASS:
case OCLASS_OPFAMILY:
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.91 2009/06/11 14:48:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.92 2009/12/11 03:34:55 itagaki Exp $
*
* NOTES
* This should be moved to a more appropriate place. It is here
#include <sys/stat.h>
#include <unistd.h>
+#include "catalog/pg_largeobject_metadata.h"
#include "libpq/be-fsstubs.h"
#include "libpq/libpq-fs.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "storage/large_object.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+/*
+ * compatibility flag for permission checks
+ */
+bool lo_compat_privileges;
/*#define FSDB 1*/
#define BUFSIZE 8192
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd)));
+ /* Permission checks */
+ if (!lo_compat_privileges &&
+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
+ GetUserId(),
+ ACL_SELECT,
+ cookies[fd]->snapshot) != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for large object %u",
+ cookies[fd]->id)));
+
status = inv_read(cookies[fd], buf, len);
return status;
errmsg("large object descriptor %d was not opened for writing",
fd)));
+ /* Permission checks */
+ if (!lo_compat_privileges &&
+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
+ GetUserId(),
+ ACL_UPDATE,
+ cookies[fd]->snapshot) != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for large object %u",
+ cookies[fd]->id)));
+
status = inv_write(cookies[fd], buf, len);
return status;
{
Oid lobjId = PG_GETARG_OID(0);
+ /* Must be owner of the largeobject */
+ if (!lo_compat_privileges &&
+ !pg_largeobject_ownercheck(lobjId, GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of large object %u", lobjId)));
+
/*
* If there are any open LO FDs referencing that ID, close 'em.
*/
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd)));
+ /* Permission checks */
+ if (!lo_compat_privileges &&
+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
+ GetUserId(),
+ ACL_UPDATE,
+ cookies[fd]->snapshot) != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for large object %u",
+ cookies[fd]->id)));
+
inv_truncate(cookies[fd], len);
PG_RETURN_INT32(0);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.695 2009/12/07 05:22:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.696 2009/12/11 03:34:55 itagaki Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
%type <boolean> opt_varying opt_timezone
%type <ival> Iconst SignedIconst
+%type <list> Iconst_list
%type <str> Sconst comment_text
%type <str> RoleId opt_granted_by opt_boolean ColId_or_Sconst
%type <list> var_list
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));
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);
| '-' Iconst { $$ = - $2; }
;
+Iconst_list: Iconst { $$ = list_make1(makeInteger($1)); }
+ | Iconst_list ',' Iconst { $$ = lappend($1, makeInteger($3)); }
+ ;
+
/*
* Name classification hierarchy.
*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.138 2009/06/11 14:49:02 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.139 2009/12/11 03:34:55 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/sysattr.h"
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_largeobject.h"
+#include "catalog/pg_largeobject_metadata.h"
#include "commands/comment.h"
#include "libpq/libpq-fs.h"
+#include "miscadmin.h"
#include "storage/large_object.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/resowner.h"
#include "utils/snapmgr.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
static bool
myLargeObjectExists(Oid loid, Snapshot snapshot)
{
+ Relation pg_lo_meta;
+ ScanKeyData skey[1];
+ SysScanDesc sd;
+ HeapTuple tuple;
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,
+ ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(loid));
- pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock);
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ AccessShareLock);
- sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
+ sd = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
snapshot, 1, skey);
- if (systable_getnext(sd) != NULL)
+ tuple = systable_getnext(sd);
+ if (HeapTupleIsValid(tuple))
retval = true;
systable_endscan(sd);
- heap_close(pg_largeobject, AccessShareLock);
+ heap_close(pg_lo_meta, AccessShareLock);
return retval;
}
Oid
inv_create(Oid lobjId)
{
+ Oid lobjId_new;
+
/*
- * 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.
+ * Create a new largeobject with empty data pages
*/
- if (!OidIsValid(lobjId))
- {
- open_lo_relation();
-
- lobjId = GetNewOidWithIndex(lo_heap_r, LargeObjectLOidPNIndexId,
- Anum_pg_largeobject_loid);
- }
+ lobjId_new = LargeObjectCreate(lobjId);
/*
- * Create the LO by writing an empty first page for it in pg_largeobject
- * (will fail if duplicate)
+ * dependency on the owner of largeobject
+ *
+ * The reason why we use LargeObjectRelationId instead of
+ * LargeObjectMetadataRelationId here is to provide backward
+ * compatibility to the applications which utilize a knowledge
+ * about internal layout of system catalogs.
+ * OID of pg_largeobject_metadata and loid of pg_largeobject
+ * are same value, so there are no actual differences here.
*/
- LargeObjectCreate(lobjId);
-
+ recordDependencyOnOwner(LargeObjectRelationId,
+ lobjId_new, GetUserId());
/*
* Advance command counter to make new tuple visible to later operations.
*/
CommandCounterIncrement();
- return lobjId;
+ return lobjId_new;
}
/*
int
inv_drop(Oid lobjId)
{
- LargeObjectDrop(lobjId);
+ ObjectAddress object;
- /* Delete any comments on the large object */
- DeleteComments(lobjId, LargeObjectRelationId, 0);
+ /*
+ * Delete any comments and dependencies on the large object
+ */
+ object.classId = LargeObjectRelationId;
+ object.objectId = lobjId;
+ object.objectSubId = 0;
+ performDeletion(&object, DROP_CASCADE);
/*
* Advance command counter so that tuple removal will be seen by later
static uint32
inv_getsize(LargeObjectDesc *obj_desc)
{
- bool found = false;
uint32 lastbyte = 0;
ScanKeyData skey[1];
SysScanDesc sd;
* 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)
+ tuple = systable_getnext_ordered(sd, BackwardScanDirection);
+ if (HeapTupleIsValid(tuple))
{
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);
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;
}
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;
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);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.322 2009/12/09 21:57:51 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.323 2009/12/11 03:34:55 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
case OBJECT_LANGUAGE:
tag = "ALTER LANGUAGE";
break;
+ case OBJECT_LARGEOBJECT:
+ tag = "ALTER LARGEOBJECT";
+ break;
case OBJECT_OPERATOR:
tag = "ALTER OPERATOR";
break;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.151 2009/12/05 21:43:35 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.152 2009/12/11 03:34:55 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
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;
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.526 2009/12/09 21:57:51 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.527 2009/12/11 03:34:56 itagaki Exp $
*
*--------------------------------------------------------------------
*/
#include "commands/trigger.h"
#include "funcapi.h"
#include "libpq/auth.h"
+#include "libpq/be-fsstubs.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
false, NULL, NULL
},
+ {
+ {"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
+ gettext_noop("Enables backward compatibility in privilege checks on large objects"),
+ gettext_noop("When turned on, privilege checks on large objects perform "
+ "with backward compatibility as 8.4.x or earlier releases.")
+ },
+ &lo_compat_privileges,
+ false, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL
#backslash_quote = safe_encoding # on, off, or safe_encoding
#default_with_oids = off
#escape_string_warning = on
+#lo_compat_privileges = off
#sql_inheritance = on
#standard_conforming_strings = off
#synchronize_seqscans = on
* Portions Copyright (c) 1994, Regents of the University of California
* Portions taken from FreeBSD.
*
- * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.177 2009/11/14 15:39:36 mha Exp $
+ * $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.178 2009/12/11 03:34:56 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
" WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n",
"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n",
"GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n",
+ "REVOKE ALL ON pg_largeobject FROM PUBLIC;\n",
NULL
};
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.51 2009/10/12 23:41:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.52 2009/12/11 03:34:56 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "SERVER") == 0)
CONVERT_PRIV('U', "USAGE");
+ else if (strcmp(type, "LARGE OBJECT") == 0)
+ {
+ CONVERT_PRIV('r', "SELECT");
+ CONVERT_PRIV('w', "UPDATE");
+ }
else
abort();
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.554 2009/12/07 05:22:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.555 2009/12/11 03:34:56 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
/*
* dumpBlobComments
- * dump all blob comments
+ * dump all blob properties.
+ * It has "BLOB COMMENTS" tag due to the historical reason, but note
+ * that it is the routine to dump all the properties of blobs.
*
* Since we don't provide any way to be selective about dumping blobs,
* there's no need to be selective about their comments either. We put
{
const char *blobQry;
const char *blobFetchQry;
- PQExpBuffer commentcmd = createPQExpBuffer();
+ PQExpBuffer cmdQry = createPQExpBuffer();
PGresult *res;
int i;
if (g_verbose)
- write_msg(NULL, "saving large object comments\n");
+ write_msg(NULL, "saving large object properties\n");
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
/* Cursor to get all BLOB comments */
- if (AH->remoteVersion >= 70300)
+ if (AH->remoteVersion >= 80500)
+ blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, "
+ "obj_description(oid, 'pg_largeobject'), "
+ "pg_get_userbyid(lomowner), lomacl "
+ "FROM pg_largeobject_metadata";
+ else if (AH->remoteVersion >= 70300)
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
- "obj_description(loid, 'pg_largeobject') "
+ "obj_description(loid, 'pg_largeobject'), NULL, NULL "
"FROM (SELECT DISTINCT loid FROM "
"pg_description d JOIN pg_largeobject l ON (objoid = loid) "
"WHERE classoid = 'pg_largeobject'::regclass) ss";
else if (AH->remoteVersion >= 70200)
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
- "obj_description(loid, 'pg_largeobject') "
+ "obj_description(loid, 'pg_largeobject'), NULL, NULL "
"FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
else if (AH->remoteVersion >= 70100)
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
- "obj_description(loid) "
+ "obj_description(loid), NULL, NULL "
"FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
else
blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, "
" SELECT description "
" FROM pg_description pd "
" WHERE pd.objoid=pc.oid "
- " ) "
+ " ), NULL, NULL "
"FROM pg_class pc WHERE relkind = 'l'";
res = PQexec(g_conn, blobQry);
/* Process the tuples, if any */
for (i = 0; i < PQntuples(res); i++)
{
- Oid blobOid;
- char *comment;
+ Oid blobOid = atooid(PQgetvalue(res, i, 0));
+ char *lo_comment = PQgetvalue(res, i, 1);
+ char *lo_owner = PQgetvalue(res, i, 2);
+ char *lo_acl = PQgetvalue(res, i, 3);
+ char lo_name[32];
- /* ignore blobs without comments */
- if (PQgetisnull(res, i, 1))
- continue;
+ resetPQExpBuffer(cmdQry);
- blobOid = atooid(PQgetvalue(res, i, 0));
- comment = PQgetvalue(res, i, 1);
+ /* comment on the blob */
+ if (!PQgetisnull(res, i, 1))
+ {
+ appendPQExpBuffer(cmdQry,
+ "COMMENT ON LARGE OBJECT %u IS ", blobOid);
+ appendStringLiteralAH(cmdQry, lo_comment, AH);
+ appendPQExpBuffer(cmdQry, ";\n");
+ }
+
+ /* dump blob ownership, if necessary */
+ if (!PQgetisnull(res, i, 2))
+ {
+ appendPQExpBuffer(cmdQry,
+ "ALTER LARGE OBJECT %u OWNER TO %s;\n",
+ blobOid, lo_owner);
+ }
- printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ",
- blobOid);
- appendStringLiteralAH(commentcmd, comment, AH);
- appendPQExpBuffer(commentcmd, ";\n");
+ /* dump blob privileges, if necessary */
+ if (!PQgetisnull(res, i, 3) &&
+ !dataOnly && !aclsSkip)
+ {
+ snprintf(lo_name, sizeof(lo_name), "%u", blobOid);
+ if (!buildACLCommands(lo_name, NULL, "LARGE OBJECT",
+ lo_acl, lo_owner, "",
+ AH->remoteVersion, cmdQry))
+ {
+ write_msg(NULL, "could not parse ACL (%s) for "
+ "large object %u", lo_acl, blobOid);
+ exit_nicely();
+ }
+ }
- archputs(commentcmd->data, AH);
+ if (cmdQry->len > 0)
+ {
+ appendPQExpBuffer(cmdQry, "\n");
+ archputs(cmdQry->data, AH);
+ }
}
} while (PQntuples(res) > 0);
archputs("\n", AH);
- destroyPQExpBuffer(commentcmd);
+ destroyPQExpBuffer(cmdQry);
return 1;
}
*
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/large_obj.c,v 1.52 2009/01/01 17:23:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/large_obj.c,v 1.53 2009/12/11 03:34:56 itagaki Exp $
*/
#include "postgres_fe.h"
#include "large_obj.h"
char buf[1024];
printQueryOpt myopt = pset.popt;
- snprintf(buf, sizeof(buf),
- "SELECT loid as \"%s\",\n"
- " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
- "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
- "ORDER BY 1",
- gettext_noop("ID"),
- gettext_noop("Description"));
+ if (pset.sversion >= 80500)
+ {
+ snprintf(buf, sizeof(buf),
+ "SELECT oid as \"%s\",\n"
+ " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n"
+ " pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
+ " FROM pg_catalog.pg_largeobject_metadata "
+ " ORDER BY oid",
+ gettext_noop("ID"),
+ gettext_noop("Owner"),
+ gettext_noop("Description"));
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf),
+ "SELECT loid as \"%s\",\n"
+ " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
+ "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
+ "ORDER BY 1",
+ gettext_noop("ID"),
+ gettext_noop("Description"));
+ }
res = PSQLexec(buf, false);
if (!res)
*
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.187 2009/10/13 21:04:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.188 2009/12/11 03:34:56 itagaki Exp $
*/
/*----------------------------------------------------------------------
{
static const char *const list_ALTER[] =
{"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
- "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
+ "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
COMPLETE_WITH_LIST(list_ALTER);
COMPLETE_WITH_LIST(list_ALTERLANGUAGE);
}
+ /* ALTER LARGE OBJECT <oid> */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "LARGE") == 0 &&
+ pg_strcasecmp(prev2_wd, "OBJECT") == 0)
+ {
+ static const char *const list_ALTERLARGEOBJECT[] =
+ {"OWNER TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT);
+ }
+
/* ALTER USER,ROLE <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
!(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) &&
" UNION SELECT 'FOREIGN SERVER'"
" UNION SELECT 'FUNCTION'"
" UNION SELECT 'LANGUAGE'"
+ " UNION SELECT 'LARGE OBJECT'"
" UNION SELECT 'SCHEMA'"
" UNION SELECT 'TABLESPACE'");
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.556 2009/12/07 05:22:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.557 2009/12/11 03:34:56 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200912071
+#define CATALOG_VERSION_NO 200912111
#endif
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.42 2009/10/07 22:14:24 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.43 2009/12/11 03:34:56 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
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 */
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.110 2009/10/07 22:14:25 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.111 2009/12/11 03:34:56 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
DECLARE_UNIQUE_INDEX(pg_largeobject_loid_pn_index, 2683, on pg_largeobject using btree(loid oid_ops, pageno int4_ops));
#define LargeObjectLOidPNIndexId 2683
+DECLARE_UNIQUE_INDEX(pg_largeobject_metadata_oid_index, 2996, on pg_largeobject_metadata using btree(oid oid_ops));
+#define LargeObjectMetadataOidIndexId 2996
+
DECLARE_UNIQUE_INDEX(pg_namespace_nspname_index, 2684, on pg_namespace using btree(nspname name_ops));
#define NamespaceNameIndexId 2684
DECLARE_UNIQUE_INDEX(pg_namespace_oid_index, 2685, on pg_namespace using btree(oid oid_ops));
* 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.h,v 1.24 2009/01/01 17:23:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_largeobject.h,v 1.25 2009/12/11 03:34:56 itagaki Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
#define Anum_pg_largeobject_pageno 2
#define Anum_pg_largeobject_data 3
-extern void LargeObjectCreate(Oid loid);
+extern Oid LargeObjectCreate(Oid loid);
extern void LargeObjectDrop(Oid loid);
+extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId);
extern bool LargeObjectExists(Oid loid);
#endif /* PG_LARGEOBJECT_H */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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.1 2009/12/11 03:34:56 itagaki 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 2995
+
+CATALOG(pg_largeobject_metadata,2995)
+{
+ Oid lomowner; /* OID of the largeobject owner */
+ aclitem lomacl[1]; /* access permissions */
+} FormData_pg_largeobject_metadata;
+
+/* ----------------
+ * Form_pg_largeobject_metadata corresponds to a pointer to a tuple
+ * with the format of pg_largeobject_metadata relation.
+ * ----------------
+ */
+typedef FormData_pg_largeobject_metadata *Form_pg_largeobject_metadata;
+
+/* ----------------
+ * compiler constants for pg_largeobject_metadata
+ * ----------------
+ */
+#define Natts_pg_largeobject_metadata 2
+#define Anum_pg_largeobject_metadata_lomowner 1
+#define Anum_pg_largeobject_metadata_lomacl 2
+
+#endif /* PG_LARGEOBJECT_METADATA_H */
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.32 2009/01/01 17:23:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.33 2009/12/11 03:34:56 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
extern Datum lo_unlink(PG_FUNCTION_ARGS);
extern Datum lo_truncate(PG_FUNCTION_ARGS);
+/*
+ * compatibility option for access control
+ */
+extern bool lo_compat_privileges;
+
/*
* These are not fmgr-callable, but are available to C code.
* Probably these should have had the underscore-free names,
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.417 2009/12/07 05:22:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.418 2009/12/11 03:34:56 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
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;
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.110 2009/12/05 21:43:36 petere Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.111 2009/12/11 03:34:56 itagaki Exp $
*
* NOTES
* An ACL array is simply an array of AclItems, representing the union
#include "nodes/parsenodes.h"
#include "utils/array.h"
+#include "utils/snapshot.h"
/*
#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)
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 */
AclMode mask, AclMaskHow how);
extern AclMode pg_language_aclmask(Oid lang_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
+ AclMode mask, AclMaskHow how, Snapshot snapshot);
extern AclMode pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode);
extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode);
extern AclResult pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode);
+extern AclResult pg_largeobject_aclcheck_snapshot(Oid lang_oid, Oid roleid,
+ AclMode mode, Snapshot snapshot);
extern AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode);
extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern bool pg_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);
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;
t
(1 row)
+-- largeobject privilege tests
+\c -
+SET SESSION AUTHORIZATION regressuser1;
+SELECT lo_create(1001);
+ lo_create
+-----------
+ 1001
+(1 row)
+
+SELECT lo_create(1002);
+ lo_create
+-----------
+ 1002
+(1 row)
+
+SELECT lo_create(1003);
+ lo_create
+-----------
+ 1003
+(1 row)
+
+SELECT lo_create(1004);
+ lo_create
+-----------
+ 1004
+(1 row)
+
+SELECT lo_create(1005);
+ lo_create
+-----------
+ 1005
+(1 row)
+
+GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC;
+GRANT SELECT ON LARGE OBJECT 1003 TO regressuser2;
+GRANT SELECT,UPDATE ON LARGE OBJECT 1004 TO regressuser2;
+GRANT ALL ON LARGE OBJECT 1005 TO regressuser2;
+GRANT SELECT ON LARGE OBJECT 1005 TO regressuser2 WITH GRANT OPTION;
+GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed
+ERROR: invalid privilege type INSERT for large object
+GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed
+ERROR: role "nosuchuser" does not exist
+GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed
+ERROR: large object 999 does not exist
+\c -
+SET SESSION AUTHORIZATION regressuser2;
+SELECT lo_create(2001);
+ lo_create
+-----------
+ 2001
+(1 row)
+
+SELECT lo_create(2002);
+ lo_create
+-----------
+ 2002
+(1 row)
+
+SELECT loread(lo_open(1001, x'40000'::int), 32);
+ loread
+--------
+ \x
+(1 row)
+
+SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
+ERROR: permission denied for large object 1002
+SELECT loread(lo_open(1003, x'40000'::int), 32);
+ loread
+--------
+ \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 large object 1002
+SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied
+ERROR: permission denied for large object 1003
+SELECT lowrite(lo_open(1004, x'20000'::int), 'abcd');
+ lowrite
+---------
+ 4
+(1 row)
+
+GRANT SELECT ON LARGE OBJECT 1005 TO regressuser3;
+GRANT UPDATE ON LARGE OBJECT 1006 TO regressuser3; -- to be denied
+ERROR: large object 1006 does not exist
+REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC;
+GRANT ALL ON LARGE OBJECT 2001 TO regressuser3;
+SELECT lo_unlink(1001); -- to be denied
+ERROR: must be owner of large object 1001
+SELECT lo_unlink(2002);
+ lo_unlink
+-----------
+ 1
+(1 row)
+
+\c -
+-- confirm ACL setting
+SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata;
+ oid | ownername | lomacl
+------+--------------+------------------------------------------------------------------------------------------
+ 1002 | regressuser1 |
+ 1001 | regressuser1 | {regressuser1=rw/regressuser1,=rw/regressuser1}
+ 1003 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=r/regressuser1}
+ 1004 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=rw/regressuser1}
+ 1005 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=r*w/regressuser1,regressuser3=r/regressuser2}
+ 2001 | regressuser2 | {regressuser2=rw/regressuser2,regressuser3=rw/regressuser2}
+(6 rows)
+
+SET SESSION AUTHORIZATION regressuser3;
+SELECT loread(lo_open(1001, x'40000'::int), 32);
+ loread
+------------
+ \x61626364
+(1 row)
+
+SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied
+ERROR: permission denied for large object 1003
+SELECT loread(lo_open(1005, x'40000'::int), 32);
+ loread
+--------
+ \x
+(1 row)
+
+SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied
+ERROR: permission denied for large object 1005
+SELECT lo_truncate(lo_open(2001, x'20000'::int), 10);
+ lo_truncate
+-------------
+ 0
+(1 row)
+
+-- compatibility mode in largeobject permission
+\c -
+SET lo_compat_privileges = false; -- default setting
+SET SESSION AUTHORIZATION regressuser4;
+SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
+ERROR: permission denied for large object 1002
+SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied
+ERROR: permission denied for large object 1002
+SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied
+ERROR: permission denied for large object 1002
+SELECT lo_unlink(1002); -- to be denied
+ERROR: must be owner of large object 1002
+SELECT lo_export(1001, '/dev/null'); -- to be denied
+ERROR: must be superuser to use server-side lo_export()
+HINT: Anyone can use the client-side lo_export() provided by libpq.
+\c -
+SET lo_compat_privileges = true; -- compatibility mode
+SET SESSION AUTHORIZATION regressuser4;
+SELECT loread(lo_open(1002, x'40000'::int), 32);
+ loread
+--------
+ \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 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 regressuser3;
DROP USER regressuser4;
DROP USER regressuser5;
+DROP USER regressuser6;
+ERROR: role "regressuser6" does not exist
pg_inherits | t
pg_language | t
pg_largeobject | t
+ pg_largeobject_metadata | t
pg_listener | f
pg_namespace | t
pg_opclass | t
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(142 rows)
+(143 rows)
--
-- another sanity check: every system catalog that has OIDs should have
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 DROP USER regressuser2;
ALTER GROUP regressgroup2 ADD USER regressuser4;
-
-- test owner privileges
SET SESSION AUTHORIZATION regressuser1;
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 lo_compat_privileges = false; -- default setting
+SET SESSION AUTHORIZATION regressuser4;
+
+SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
+SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied
+SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied
+SELECT lo_unlink(1002); -- to be denied
+SELECT lo_export(1001, '/dev/null'); -- to be denied
+
+\c -
+SET lo_compat_privileges = true; -- compatibility mode
+SET SESSION AUTHORIZATION regressuser4;
+
+SELECT loread(lo_open(1002, x'40000'::int), 32);
+SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd');
+SELECT lo_truncate(lo_open(1002, x'20000'::int), 10);
+SELECT lo_unlink(1002);
+SELECT lo_export(1001, '/dev/null'); -- to be denied
-- test default ACLs
\c -
DROP TABLE atestp1;
DROP TABLE atestp2;
+SELECT lo_unlink(oid) FROM pg_largeobject_metadata;
+
DROP GROUP regressgroup1;
DROP GROUP regressgroup2;
DROP USER regressuser3;
DROP USER regressuser4;
DROP USER regressuser5;
+DROP USER regressuser6;