Commit f1325ce2 authored by Itagaki Takahiro's avatar Itagaki Takahiro

Add large object access control.

A new system catalog pg_largeobject_metadata manages
ownership and access privileges of large objects.

KaiGai Kohei, reviewed by Jaime Casanova.
parent 64579962
<!-- $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 Documentation of the system catalogs, directed toward PostgreSQL developers
--> -->
...@@ -160,7 +160,12 @@ ...@@ -160,7 +160,12 @@
<row> <row>
<entry><link linkend="catalog-pg-largeobject"><structname>pg_largeobject</structname></link></entry> <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>
<row> <row>
...@@ -3120,22 +3125,31 @@ ...@@ -3120,22 +3125,31 @@
<para> <para>
The catalog <structname>pg_largeobject</structname> holds the data making up The catalog <structname>pg_largeobject</structname> holds the data making up
<quote>large objects</quote>. A large object is identified by an <quote>large objects</quote>. A large object is identified by an OID of
OID assigned when it is created. Each large object is broken into <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 segments or <quote>pages</> small enough to be conveniently stored as rows
in <structname>pg_largeobject</structname>. in <structname>pg_largeobject</structname>.
The amount of data per page is defined to be <symbol>LOBLKSIZE</> (which is currently The amount of data per page is defined to be <symbol>LOBLKSIZE</> (which is currently
<literal>BLCKSZ/4</>, or typically 2 kB). <literal>BLCKSZ/4</>, or typically 2 kB).
</para> </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> <table>
<title><structname>pg_largeobject</> Columns</title> <title><structname>pg_largeobject</> Columns</title>
<tgroup cols="3"> <tgroup cols="4">
<thead> <thead>
<row> <row>
<entry>Name</entry> <entry>Name</entry>
<entry>Type</entry> <entry>Type</entry>
<entry>References</entry>
<entry>Description</entry> <entry>Description</entry>
</row> </row>
</thead> </thead>
...@@ -3144,12 +3158,14 @@ ...@@ -3144,12 +3158,14 @@
<row> <row>
<entry><structfield>loid</structfield></entry> <entry><structfield>loid</structfield></entry>
<entry><type>oid</type></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> <entry>Identifier of the large object that includes this page</entry>
</row> </row>
<row> <row>
<entry><structfield>pageno</structfield></entry> <entry><structfield>pageno</structfield></entry>
<entry><type>int4</type></entry> <entry><type>int4</type></entry>
<entry></entry>
<entry>Page number of this page within its large object <entry>Page number of this page within its large object
(counting from zero)</entry> (counting from zero)</entry>
</row> </row>
...@@ -3157,6 +3173,7 @@ ...@@ -3157,6 +3173,7 @@
<row> <row>
<entry><structfield>data</structfield></entry> <entry><structfield>data</structfield></entry>
<entry><type>bytea</type></entry> <entry><type>bytea</type></entry>
<entry></entry>
<entry> <entry>
Actual data stored in the large object. Actual data stored in the large object.
This will never be more than <symbol>LOBLKSIZE</> bytes and might be less This will never be more than <symbol>LOBLKSIZE</> bytes and might be less
...@@ -3177,6 +3194,55 @@ ...@@ -3177,6 +3194,55 @@
</sect1> </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"> <sect1 id="catalog-pg-listener">
<title><structname>pg_listener</structname></title> <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"> <chapter Id="runtime-config">
<title>Server Configuration</title> <title>Server Configuration</title>
...@@ -4816,6 +4816,35 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' ...@@ -4816,6 +4816,35 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
</listitem> </listitem>
</varlistentry> </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"> <varlistentry id="guc-sql-inheritance" xreflabel="sql_inheritance">
<term><varname>sql_inheritance</varname> (<type>boolean</type>)</term> <term><varname>sql_inheritance</varname> (<type>boolean</type>)</term>
<indexterm> <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"> <chapter id="largeObjects">
<title id="largeObjects-title">Large Objects</title> <title id="largeObjects-title">Large Objects</title>
...@@ -441,6 +441,57 @@ SELECT lo_export(image.raster, '/tmp/motd') FROM image ...@@ -441,6 +441,57 @@ SELECT lo_export(image.raster, '/tmp/motd') FROM image
The client-side functions can be used by any The client-side functions can be used by any
<productname>PostgreSQL</productname> user. <productname>PostgreSQL</productname> user.
</para> </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>
<sect1 id="lo-examplesect"> <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 PostgreSQL documentation
Complete list of usable sgml source files in this directory. Complete list of usable sgml source files in this directory.
--> -->
...@@ -16,6 +16,7 @@ Complete list of usable sgml source files in this directory. ...@@ -16,6 +16,7 @@ Complete list of usable sgml source files in this directory.
<!entity alterGroup system "alter_group.sgml"> <!entity alterGroup system "alter_group.sgml">
<!entity alterIndex system "alter_index.sgml"> <!entity alterIndex system "alter_index.sgml">
<!entity alterLanguage system "alter_language.sgml"> <!entity alterLanguage system "alter_language.sgml">
<!entity alterLargeObject system "alter_large_object.sgml">
<!entity alterOperator system "alter_operator.sgml"> <!entity alterOperator system "alter_operator.sgml">
<!entity alterOperatorClass system "alter_opclass.sgml"> <!entity alterOperatorClass system "alter_opclass.sgml">
<!entity alterOperatorFamily system "alter_opfamily.sgml"> <!entity alterOperatorFamily system "alter_opfamily.sgml">
......
<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 PostgreSQL documentation
--> -->
...@@ -59,6 +59,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] } ...@@ -59,6 +59,10 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
ON LANGUAGE <replaceable>lang_name</replaceable> [, ...] ON LANGUAGE <replaceable>lang_name</replaceable> [, ...]
TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ] 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 ] } GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
ON SCHEMA <replaceable>schema_name</replaceable> [, ...] ON SCHEMA <replaceable>schema_name</replaceable> [, ...]
TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ] TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
...@@ -170,6 +174,8 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace ...@@ -170,6 +174,8 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
<xref linkend="sql-delete" endterm="sql-delete-title">. <xref linkend="sql-delete" endterm="sql-delete-title">.
For sequences, this privilege also allows the use of the For sequences, this privilege also allows the use of the
<function>currval</function> function. <function>currval</function> function.
For large objects, this privilege also allows to read from
the target large object.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -203,6 +209,8 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace ...@@ -203,6 +209,8 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
<literal>SELECT</literal> privilege. For sequences, this <literal>SELECT</literal> privilege. For sequences, this
privilege allows the use of the <function>nextval</function> and privilege allows the use of the <function>nextval</function> and
<function>setval</function> functions. <function>setval</function> functions.
For large objects, this privilege also allows to write or truncate
on the target large object.
</para> </para>
</listitem> </listitem>
</varlistentry> </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 PostgreSQL documentation
--> -->
...@@ -75,6 +75,12 @@ REVOKE [ GRANT OPTION FOR ] ...@@ -75,6 +75,12 @@ REVOKE [ GRANT OPTION FOR ]
FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
[ CASCADE | RESTRICT ] [ 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 ] REVOKE [ GRANT OPTION FOR ]
{ { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] } { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
ON SCHEMA <replaceable>schema_name</replaceable> [, ...] 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"> <part id="reference">
<title>Reference</title> <title>Reference</title>
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
&alterGroup; &alterGroup;
&alterIndex; &alterIndex;
&alterLanguage; &alterLanguage;
&alterLargeObject;
&alterOperator; &alterOperator;
&alterOperatorClass; &alterOperatorClass;
&alterOperatorFamily; &alterOperatorFamily;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
# Makefile for backend/catalog # 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 $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -29,9 +29,9 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ ...@@ -29,9 +29,9 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_proc.h pg_type.h pg_attribute.h pg_class.h \ 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_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_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_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ pg_statistic.h pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h \
pg_enum.h pg_namespace.h pg_conversion.h pg_depend.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_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_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 \ pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h" #include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h" #include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
...@@ -131,6 +132,7 @@ static const Oid object_classes[MAX_OCLASS] = { ...@@ -131,6 +132,7 @@ static const Oid object_classes[MAX_OCLASS] = {
ConversionRelationId, /* OCLASS_CONVERSION */ ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */ AttrDefaultRelationId, /* OCLASS_DEFAULT */
LanguageRelationId, /* OCLASS_LANGUAGE */ LanguageRelationId, /* OCLASS_LANGUAGE */
LargeObjectRelationId, /* OCLASS_LARGEOBJECT */
OperatorRelationId, /* OCLASS_OPERATOR */ OperatorRelationId, /* OCLASS_OPERATOR */
OperatorClassRelationId, /* OCLASS_OPCLASS */ OperatorClassRelationId, /* OCLASS_OPCLASS */
OperatorFamilyRelationId, /* OCLASS_OPFAMILY */ OperatorFamilyRelationId, /* OCLASS_OPFAMILY */
...@@ -1074,6 +1076,10 @@ doDeletion(const ObjectAddress *object) ...@@ -1074,6 +1076,10 @@ doDeletion(const ObjectAddress *object)
DropProceduralLanguageById(object->objectId); DropProceduralLanguageById(object->objectId);
break; break;
case OCLASS_LARGEOBJECT:
LargeObjectDrop(object->objectId);
break;
case OCLASS_OPERATOR: case OCLASS_OPERATOR:
RemoveOperatorById(object->objectId); RemoveOperatorById(object->objectId);
break; break;
...@@ -1991,6 +1997,10 @@ getObjectClass(const ObjectAddress *object) ...@@ -1991,6 +1997,10 @@ getObjectClass(const ObjectAddress *object)
Assert(object->objectSubId == 0); Assert(object->objectSubId == 0);
return OCLASS_LANGUAGE; return OCLASS_LANGUAGE;
case LargeObjectRelationId:
Assert(object->objectSubId == 0);
return OCLASS_LARGEOBJECT;
case OperatorRelationId: case OperatorRelationId:
Assert(object->objectSubId == 0); Assert(object->objectSubId == 0);
return OCLASS_OPERATOR; return OCLASS_OPERATOR;
...@@ -2243,6 +2253,10 @@ getObjectDescription(const ObjectAddress *object) ...@@ -2243,6 +2253,10 @@ getObjectDescription(const ObjectAddress *object)
ReleaseSysCache(langTup); ReleaseSysCache(langTup);
break; break;
} }
case OCLASS_LARGEOBJECT:
appendStringInfo(&buffer, _("large object %u"),
object->objectId);
break;
case OCLASS_OPERATOR: case OCLASS_OPERATOR:
appendStringInfo(&buffer, _("operator %s"), appendStringInfo(&buffer, _("operator %s"),
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,8 +16,16 @@ ...@@ -16,8 +16,16 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/sysattr.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_largeobject.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/bytea.h"
#include "utils/fmgroids.h" #include "utils/fmgroids.h"
#include "utils/rel.h" #include "utils/rel.h"
...@@ -27,113 +35,258 @@ ...@@ -27,113 +35,258 @@
/* /*
* Create a large object having the given LO identifier. * Create a large object having the given LO identifier.
* *
* We do this by inserting an empty first page, so that the object will * We create a new large object by inserting an entry into
* appear to exist with size 0. Note that the unique index will reject * pg_largeobject_metadata without any data pages, so that the object
* an attempt to create a duplicate page. * will appear to exist with size 0.
*/ */
void Oid
LargeObjectCreate(Oid loid) LargeObjectCreate(Oid loid)
{ {
Relation pg_largeobject; Relation pg_lo_meta;
HeapTuple ntup; HeapTuple ntup;
Datum values[Natts_pg_largeobject]; Oid loid_new;
bool nulls[Natts_pg_largeobject]; Datum values[Natts_pg_largeobject_metadata];
int i; 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++) memset(values, 0, sizeof(values));
{ memset(nulls, false, sizeof(nulls));
values[i] = (Datum) NULL;
nulls[i] = false;
}
i = 0; values[Anum_pg_largeobject_metadata_lomowner - 1]
values[i++] = ObjectIdGetDatum(loid); = ObjectIdGetDatum(GetUserId());
values[i++] = Int32GetDatum(0); nulls[Anum_pg_largeobject_metadata_lomacl - 1] = true;
values[i++] = DirectFunctionCall1(byteain,
CStringGetDatum(""));
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);
/* loid_new = simple_heap_insert(pg_lo_meta, ntup);
* Insert it Assert(!OidIsValid(loid) || loid == loid_new);
*/
simple_heap_insert(pg_largeobject, ntup);
/* Update indexes */ CatalogUpdateIndexes(pg_lo_meta, ntup);
CatalogUpdateIndexes(pg_largeobject, ntup);
heap_close(pg_largeobject, RowExclusiveLock);
heap_freetuple(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 void
LargeObjectDrop(Oid loid) LargeObjectDrop(Oid loid)
{ {
bool found = false; Relation pg_lo_meta;
Relation pg_largeobject; Relation pg_largeobject;
ScanKeyData skey[1]; ScanKeyData skey[1];
SysScanDesc sd; SysScanDesc scan;
HeapTuple tuple; 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], ScanKeyInit(&skey[0],
Anum_pg_largeobject_loid, ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ, BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(loid)); ObjectIdGetDatum(loid));
pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock); scan = systable_beginscan(pg_lo_meta,
LargeObjectMetadataOidIndexId, true,
sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
SnapshotNow, 1, skey); SnapshotNow, 1, skey);
while ((tuple = systable_getnext(sd)) != NULL) 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); simple_heap_delete(pg_largeobject, &tuple->t_self);
found = true;
} }
systable_endscan(sd); systable_endscan(scan);
heap_close(pg_largeobject, RowExclusiveLock); 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, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", loid))); 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 bool
LargeObjectExists(Oid loid) LargeObjectExists(Oid loid)
{ {
bool retval = false; Relation pg_lo_meta;
Relation pg_largeobject;
ScanKeyData skey[1]; ScanKeyData skey[1];
SysScanDesc sd; SysScanDesc sd;
HeapTuple tuple;
bool retval = false;
/*
* See if we can find any tuples belonging to the specified LO
*/
ScanKeyInit(&skey[0], ScanKeyInit(&skey[0],
Anum_pg_largeobject_loid, ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ, BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(loid)); 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); SnapshotNow, 1, skey);
if (systable_getnext(sd) != NULL) tuple = systable_getnext(sd);
if (HeapTupleIsValid(tuple))
retval = true; retval = true;
systable_endscan(sd); systable_endscan(sd);
heap_close(pg_largeobject, AccessShareLock); heap_close(pg_lo_meta, AccessShareLock);
return retval; return retval;
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "catalog/pg_database.h" #include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h" #include "catalog/pg_default_acl.h"
#include "catalog/pg_language.h" #include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
#include "catalog/pg_proc.h" #include "catalog/pg_proc.h"
...@@ -1347,6 +1348,10 @@ shdepReassignOwned(List *roleids, Oid newrole) ...@@ -1347,6 +1348,10 @@ shdepReassignOwned(List *roleids, Oid newrole)
AlterLanguageOwner_oid(sdepForm->objid, newrole); AlterLanguageOwner_oid(sdepForm->objid, newrole);
break; break;
case LargeObjectRelationId:
LargeObjectAlterOwner(sdepForm->objid, newrole);
break;
case DefaultAclRelationId: case DefaultAclRelationId:
/* /*
* Ignore default ACLs; they should be handled by * Ignore default ACLs; they should be handled by
......
...@@ -8,13 +8,14 @@ ...@@ -8,13 +8,14 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_largeobject.h"
#include "commands/alter.h" #include "commands/alter.h"
#include "commands/conversioncmds.h" #include "commands/conversioncmds.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
...@@ -233,6 +234,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) ...@@ -233,6 +234,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
AlterLanguageOwner(strVal(linitial(stmt->object)), newowner); AlterLanguageOwner(strVal(linitial(stmt->object)), newowner);
break; break;
case OBJECT_LARGEOBJECT:
LargeObjectAlterOwner(intVal(linitial(stmt->object)), newowner);
break;
case OBJECT_OPERATOR: case OBJECT_OPERATOR:
Assert(list_length(stmt->objarg) == 2); Assert(list_length(stmt->objarg) == 2);
AlterOperatorOwner(stmt->object, AlterOperatorOwner(stmt->object,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Copyright (c) 1996-2009, PostgreSQL Global Development Group * Copyright (c) 1996-2009, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "catalog/pg_description.h" #include "catalog/pg_description.h"
#include "catalog/pg_language.h" #include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h" #include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
...@@ -42,6 +43,7 @@ ...@@ -42,6 +43,7 @@
#include "commands/comment.h" #include "commands/comment.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/tablespace.h" #include "commands/tablespace.h"
#include "libpq/be-fsstubs.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "parser/parse_func.h" #include "parser/parse_func.h"
...@@ -1435,7 +1437,20 @@ CommentLargeObject(List *qualname, char *comment) ...@@ -1435,7 +1437,20 @@ CommentLargeObject(List *qualname, char *comment)
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", loid))); 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); CreateComments(loid, LargeObjectRelationId, 0, comment);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -6186,6 +6186,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, ...@@ -6186,6 +6186,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_CAST: case OCLASS_CAST:
case OCLASS_CONVERSION: case OCLASS_CONVERSION:
case OCLASS_LANGUAGE: case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
case OCLASS_OPERATOR: case OCLASS_OPERATOR:
case OCLASS_OPCLASS: case OCLASS_OPCLASS:
case OCLASS_OPFAMILY: case OCLASS_OPFAMILY:
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* This should be moved to a more appropriate place. It is here * This should be moved to a more appropriate place. It is here
...@@ -42,14 +42,20 @@ ...@@ -42,14 +42,20 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "catalog/pg_largeobject_metadata.h"
#include "libpq/be-fsstubs.h" #include "libpq/be-fsstubs.h"
#include "libpq/libpq-fs.h" #include "libpq/libpq-fs.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "storage/large_object.h" #include "storage/large_object.h"
#include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/memutils.h" #include "utils/memutils.h"
/*
* compatibility flag for permission checks
*/
bool lo_compat_privileges;
/*#define FSDB 1*/ /*#define FSDB 1*/
#define BUFSIZE 8192 #define BUFSIZE 8192
...@@ -156,6 +162,17 @@ lo_read(int fd, char *buf, int len) ...@@ -156,6 +162,17 @@ lo_read(int fd, char *buf, int len)
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd))); 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); status = inv_read(cookies[fd], buf, len);
return status; return status;
...@@ -177,6 +194,17 @@ lo_write(int fd, const char *buf, int len) ...@@ -177,6 +194,17 @@ lo_write(int fd, const char *buf, int len)
errmsg("large object descriptor %d was not opened for writing", errmsg("large object descriptor %d was not opened for writing",
fd))); 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); status = inv_write(cookies[fd], buf, len);
return status; return status;
...@@ -251,6 +279,13 @@ lo_unlink(PG_FUNCTION_ARGS) ...@@ -251,6 +279,13 @@ lo_unlink(PG_FUNCTION_ARGS)
{ {
Oid lobjId = PG_GETARG_OID(0); 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. * If there are any open LO FDs referencing that ID, close 'em.
*/ */
...@@ -482,6 +517,17 @@ lo_truncate(PG_FUNCTION_ARGS) ...@@ -482,6 +517,17 @@ lo_truncate(PG_FUNCTION_ARGS)
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd))); 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); inv_truncate(cookies[fd], len);
PG_RETURN_INT32(0); PG_RETURN_INT32(0);
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -397,6 +397,7 @@ static TypeName *TableFuncTypeName(List *columns); ...@@ -397,6 +397,7 @@ static TypeName *TableFuncTypeName(List *columns);
%type <boolean> opt_varying opt_timezone %type <boolean> opt_varying opt_timezone
%type <ival> Iconst SignedIconst %type <ival> Iconst SignedIconst
%type <list> Iconst_list
%type <str> Sconst comment_text %type <str> Sconst comment_text
%type <str> RoleId opt_granted_by opt_boolean ColId_or_Sconst %type <str> RoleId opt_granted_by opt_boolean ColId_or_Sconst
%type <list> var_list %type <list> var_list
...@@ -4576,6 +4577,14 @@ privilege_target: ...@@ -4576,6 +4577,14 @@ privilege_target:
n->objs = $2; n->objs = $2;
$$ = n; $$ = 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 | SCHEMA name_list
{ {
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
...@@ -5851,6 +5860,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId ...@@ -5851,6 +5860,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
n->newowner = $7; n->newowner = $7;
$$ = (Node *)n; $$ = (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 | ALTER OPERATOR any_operator oper_argtypes OWNER TO RoleId
{ {
AlterOwnerStmt *n = makeNode(AlterOwnerStmt); AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
...@@ -10542,6 +10559,10 @@ SignedIconst: Iconst { $$ = $1; } ...@@ -10542,6 +10559,10 @@ SignedIconst: Iconst { $$ = $1; }
| '-' Iconst { $$ = - $2; } | '-' Iconst { $$ = - $2; }
; ;
Iconst_list: Iconst { $$ = list_make1(makeInteger($1)); }
| Iconst_list ',' Iconst { $$ = lappend($1, makeInteger($3)); }
;
/* /*
* Name classification hierarchy. * Name classification hierarchy.
* *
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -32,18 +32,23 @@ ...@@ -32,18 +32,23 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h" #include "access/tuptoaster.h"
#include "access/xact.h" #include "access/xact.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "commands/comment.h" #include "commands/comment.h"
#include "libpq/libpq-fs.h" #include "libpq/libpq-fs.h"
#include "miscadmin.h"
#include "storage/large_object.h" #include "storage/large_object.h"
#include "utils/fmgroids.h" #include "utils/fmgroids.h"
#include "utils/rel.h" #include "utils/rel.h"
#include "utils/resowner.h" #include "utils/resowner.h"
#include "utils/snapmgr.h" #include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h" #include "utils/tqual.h"
...@@ -139,30 +144,31 @@ close_lo_relation(bool isCommit) ...@@ -139,30 +144,31 @@ close_lo_relation(bool isCommit)
static bool static bool
myLargeObjectExists(Oid loid, Snapshot snapshot) myLargeObjectExists(Oid loid, Snapshot snapshot)
{ {
bool retval = false; Relation pg_lo_meta;
Relation pg_largeobject;
ScanKeyData skey[1]; ScanKeyData skey[1];
SysScanDesc sd; SysScanDesc sd;
HeapTuple tuple;
bool retval = false;
/*
* See if we can find any tuples belonging to the specified LO
*/
ScanKeyInit(&skey[0], ScanKeyInit(&skey[0],
Anum_pg_largeobject_loid, ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ, BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(loid)); 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); snapshot, 1, skey);
if (systable_getnext(sd) != NULL) tuple = systable_getnext(sd);
if (HeapTupleIsValid(tuple))
retval = true; retval = true;
systable_endscan(sd); systable_endscan(sd);
heap_close(pg_largeobject, AccessShareLock); heap_close(pg_lo_meta, AccessShareLock);
return retval; return retval;
} }
...@@ -193,31 +199,31 @@ getbytealen(bytea *data) ...@@ -193,31 +199,31 @@ getbytealen(bytea *data)
Oid Oid
inv_create(Oid lobjId) 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.
*/
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 * Create a new largeobject with empty data pages
* (will fail if duplicate)
*/ */
LargeObjectCreate(lobjId); lobjId_new = LargeObjectCreate(lobjId);
/*
* dependency on the owner of largeobject
*
* The reason why we use LargeObjectRelationId instead of
* LargeObjectMetadataRelationId here is to provide backward
* compatibility to the applications which utilize a knowledge
* about internal layout of system catalogs.
* OID of pg_largeobject_metadata and loid of pg_largeobject
* are same value, so there are no actual differences here.
*/
recordDependencyOnOwner(LargeObjectRelationId,
lobjId_new, GetUserId());
/* /*
* Advance command counter to make new tuple visible to later operations. * Advance command counter to make new tuple visible to later operations.
*/ */
CommandCounterIncrement(); CommandCounterIncrement();
return lobjId; return lobjId_new;
} }
/* /*
...@@ -292,10 +298,15 @@ inv_close(LargeObjectDesc *obj_desc) ...@@ -292,10 +298,15 @@ inv_close(LargeObjectDesc *obj_desc)
int int
inv_drop(Oid lobjId) 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 * Advance command counter so that tuple removal will be seen by later
...@@ -315,7 +326,6 @@ inv_drop(Oid lobjId) ...@@ -315,7 +326,6 @@ inv_drop(Oid lobjId)
static uint32 static uint32
inv_getsize(LargeObjectDesc *obj_desc) inv_getsize(LargeObjectDesc *obj_desc)
{ {
bool found = false;
uint32 lastbyte = 0; uint32 lastbyte = 0;
ScanKeyData skey[1]; ScanKeyData skey[1];
SysScanDesc sd; SysScanDesc sd;
...@@ -339,13 +349,13 @@ inv_getsize(LargeObjectDesc *obj_desc) ...@@ -339,13 +349,13 @@ inv_getsize(LargeObjectDesc *obj_desc)
* large object in reverse pageno order. So, it's sufficient to examine * large object in reverse pageno order. So, it's sufficient to examine
* the first valid tuple (== last valid page). * 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; Form_pg_largeobject data;
bytea *datafield; bytea *datafield;
bool pfreeit; bool pfreeit;
found = true;
if (HeapTupleHasNulls(tuple)) /* paranoia */ if (HeapTupleHasNulls(tuple)) /* paranoia */
elog(ERROR, "null field found in pg_largeobject"); elog(ERROR, "null field found in pg_largeobject");
data = (Form_pg_largeobject) GETSTRUCT(tuple); data = (Form_pg_largeobject) GETSTRUCT(tuple);
...@@ -360,15 +370,10 @@ inv_getsize(LargeObjectDesc *obj_desc) ...@@ -360,15 +370,10 @@ inv_getsize(LargeObjectDesc *obj_desc)
lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield); lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
if (pfreeit) if (pfreeit)
pfree(datafield); pfree(datafield);
break;
} }
systable_endscan_ordered(sd); systable_endscan_ordered(sd);
if (!found)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", obj_desc->id)));
return lastbyte; return lastbyte;
} }
...@@ -545,6 +550,12 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) ...@@ -545,6 +550,12 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
errmsg("large object %u was not opened for writing", errmsg("large object %u was not opened for writing",
obj_desc->id))); 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) if (nbytes <= 0)
return 0; return 0;
...@@ -736,6 +747,12 @@ inv_truncate(LargeObjectDesc *obj_desc, int len) ...@@ -736,6 +747,12 @@ inv_truncate(LargeObjectDesc *obj_desc, int len)
errmsg("large object %u was not opened for writing", errmsg("large object %u was not opened for writing",
obj_desc->id))); 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(); open_lo_relation();
indstate = CatalogOpenIndexes(lo_heap_r); indstate = CatalogOpenIndexes(lo_heap_r);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1638,6 +1638,9 @@ CreateCommandTag(Node *parsetree) ...@@ -1638,6 +1638,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_LANGUAGE: case OBJECT_LANGUAGE:
tag = "ALTER LANGUAGE"; tag = "ALTER LANGUAGE";
break; break;
case OBJECT_LARGEOBJECT:
tag = "ALTER LARGEOBJECT";
break;
case OBJECT_OPERATOR: case OBJECT_OPERATOR:
tag = "ALTER OPERATOR"; tag = "ALTER OPERATOR";
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -764,6 +764,11 @@ acldefault(GrantObjectType objtype, Oid ownerId) ...@@ -764,6 +764,11 @@ acldefault(GrantObjectType objtype, Oid ownerId)
world_default = ACL_USAGE; world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_LANGUAGE; owner_default = ACL_ALL_RIGHTS_LANGUAGE;
break; 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: case ACL_OBJECT_NAMESPACE:
world_default = ACL_NO_RIGHTS; world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_NAMESPACE; owner_default = ACL_ALL_RIGHTS_NAMESPACE;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
* *
* IDENTIFICATION * 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 $
* *
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "commands/trigger.h" #include "commands/trigger.h"
#include "funcapi.h" #include "funcapi.h"
#include "libpq/auth.h" #include "libpq/auth.h"
#include "libpq/be-fsstubs.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "optimizer/cost.h" #include "optimizer/cost.h"
...@@ -1226,6 +1227,16 @@ static struct config_bool ConfigureNamesBool[] = ...@@ -1226,6 +1227,16 @@ static struct config_bool ConfigureNamesBool[] =
false, NULL, NULL 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 */ /* End-of-list marker */
{ {
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL
......
...@@ -489,6 +489,7 @@ ...@@ -489,6 +489,7 @@
#backslash_quote = safe_encoding # on, off, or safe_encoding #backslash_quote = safe_encoding # on, off, or safe_encoding
#default_with_oids = off #default_with_oids = off
#escape_string_warning = on #escape_string_warning = on
#lo_compat_privileges = off
#sql_inheritance = on #sql_inheritance = on
#standard_conforming_strings = off #standard_conforming_strings = off
#synchronize_seqscans = on #synchronize_seqscans = on
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* Portions taken from FreeBSD. * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1783,6 +1783,7 @@ setup_privileges(void) ...@@ -1783,6 +1783,7 @@ setup_privileges(void)
" WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n", " WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n",
"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n",
"GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n", "GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n",
"REVOKE ALL ON pg_largeobject FROM PUBLIC;\n",
NULL NULL
}; };
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -862,6 +862,11 @@ do { \ ...@@ -862,6 +862,11 @@ do { \
CONVERT_PRIV('U', "USAGE"); CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "SERVER") == 0) else if (strcmp(type, "SERVER") == 0)
CONVERT_PRIV('U', "USAGE"); CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "LARGE OBJECT") == 0)
{
CONVERT_PRIV('r', "SELECT");
CONVERT_PRIV('w', "UPDATE");
}
else else
abort(); abort();
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* by PostgreSQL * by PostgreSQL
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2045,7 +2045,9 @@ dumpBlobs(Archive *AH, void *arg) ...@@ -2045,7 +2045,9 @@ dumpBlobs(Archive *AH, void *arg)
/* /*
* dumpBlobComments * 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, * 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 * there's no need to be selective about their comments either. We put
...@@ -2056,30 +2058,35 @@ dumpBlobComments(Archive *AH, void *arg) ...@@ -2056,30 +2058,35 @@ dumpBlobComments(Archive *AH, void *arg)
{ {
const char *blobQry; const char *blobQry;
const char *blobFetchQry; const char *blobFetchQry;
PQExpBuffer commentcmd = createPQExpBuffer(); PQExpBuffer cmdQry = createPQExpBuffer();
PGresult *res; PGresult *res;
int i; int i;
if (g_verbose) 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 */ /* Make sure we are in proper schema */
selectSourceSchema("pg_catalog"); selectSourceSchema("pg_catalog");
/* Cursor to get all BLOB comments */ /* 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, " blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
"obj_description(loid, 'pg_largeobject') " "obj_description(loid, 'pg_largeobject'), NULL, NULL "
"FROM (SELECT DISTINCT loid FROM " "FROM (SELECT DISTINCT loid FROM "
"pg_description d JOIN pg_largeobject l ON (objoid = loid) " "pg_description d JOIN pg_largeobject l ON (objoid = loid) "
"WHERE classoid = 'pg_largeobject'::regclass) ss"; "WHERE classoid = 'pg_largeobject'::regclass) ss";
else if (AH->remoteVersion >= 70200) else if (AH->remoteVersion >= 70200)
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " 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"; "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
else if (AH->remoteVersion >= 70100) else if (AH->remoteVersion >= 70100)
blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, "
"obj_description(loid) " "obj_description(loid), NULL, NULL "
"FROM (SELECT DISTINCT loid FROM pg_largeobject) ss"; "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss";
else else
blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, " blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, "
...@@ -2087,7 +2094,7 @@ dumpBlobComments(Archive *AH, void *arg) ...@@ -2087,7 +2094,7 @@ dumpBlobComments(Archive *AH, void *arg)
" SELECT description " " SELECT description "
" FROM pg_description pd " " FROM pg_description pd "
" WHERE pd.objoid=pc.oid " " WHERE pd.objoid=pc.oid "
" ) " " ), NULL, NULL "
"FROM pg_class pc WHERE relkind = 'l'"; "FROM pg_class pc WHERE relkind = 'l'";
res = PQexec(g_conn, blobQry); res = PQexec(g_conn, blobQry);
...@@ -2107,22 +2114,51 @@ dumpBlobComments(Archive *AH, void *arg) ...@@ -2107,22 +2114,51 @@ dumpBlobComments(Archive *AH, void *arg)
/* Process the tuples, if any */ /* Process the tuples, if any */
for (i = 0; i < PQntuples(res); i++) for (i = 0; i < PQntuples(res); i++)
{ {
Oid blobOid; Oid blobOid = atooid(PQgetvalue(res, i, 0));
char *comment; 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 */ resetPQExpBuffer(cmdQry);
if (PQgetisnull(res, i, 1))
continue;
blobOid = atooid(PQgetvalue(res, i, 0)); /* comment on the blob */
comment = PQgetvalue(res, i, 1); 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 ", /* dump blob privileges, if necessary */
blobOid); if (!PQgetisnull(res, i, 3) &&
appendStringLiteralAH(commentcmd, comment, AH); !dataOnly && !aclsSkip)
appendPQExpBuffer(commentcmd, ";\n"); {
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); } while (PQntuples(res) > 0);
...@@ -2130,7 +2166,7 @@ dumpBlobComments(Archive *AH, void *arg) ...@@ -2130,7 +2166,7 @@ dumpBlobComments(Archive *AH, void *arg)
archputs("\n", AH); archputs("\n", AH);
destroyPQExpBuffer(commentcmd); destroyPQExpBuffer(cmdQry);
return 1; return 1;
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2009, PostgreSQL Global Development Group * 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 "postgres_fe.h"
#include "large_obj.h" #include "large_obj.h"
...@@ -278,6 +278,20 @@ do_lo_list(void) ...@@ -278,6 +278,20 @@ do_lo_list(void)
char buf[1024]; char buf[1024];
printQueryOpt myopt = pset.popt; printQueryOpt myopt = pset.popt;
if (pset.sversion >= 80500)
{
snprintf(buf, sizeof(buf),
"SELECT oid as \"%s\",\n"
" pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n"
" pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
" FROM pg_catalog.pg_largeobject_metadata "
" ORDER BY oid",
gettext_noop("ID"),
gettext_noop("Owner"),
gettext_noop("Description"));
}
else
{
snprintf(buf, sizeof(buf), snprintf(buf, sizeof(buf),
"SELECT loid as \"%s\",\n" "SELECT loid as \"%s\",\n"
" pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n" " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
...@@ -285,6 +299,7 @@ do_lo_list(void) ...@@ -285,6 +299,7 @@ do_lo_list(void)
"ORDER BY 1", "ORDER BY 1",
gettext_noop("ID"), gettext_noop("ID"),
gettext_noop("Description")); gettext_noop("Description"));
}
res = PSQLexec(buf, false); res = PSQLexec(buf, false);
if (!res) if (!res)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2009, PostgreSQL Global Development Group * 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 $
*/ */
/*---------------------------------------------------------------------- /*----------------------------------------------------------------------
...@@ -691,7 +691,7 @@ psql_completion(char *text, int start, int end) ...@@ -691,7 +691,7 @@ psql_completion(char *text, int start, int end)
{ {
static const char *const list_ALTER[] = static const char *const list_ALTER[] =
{"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION", {"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}; "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
COMPLETE_WITH_LIST(list_ALTER); COMPLETE_WITH_LIST(list_ALTER);
...@@ -760,6 +760,17 @@ psql_completion(char *text, int start, int end) ...@@ -760,6 +760,17 @@ psql_completion(char *text, int start, int end)
COMPLETE_WITH_LIST(list_ALTERLANGUAGE); 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> */ /* ALTER USER,ROLE <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
!(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) && !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) &&
...@@ -1732,6 +1743,7 @@ psql_completion(char *text, int start, int end) ...@@ -1732,6 +1743,7 @@ psql_completion(char *text, int start, int end)
" UNION SELECT 'FOREIGN SERVER'" " UNION SELECT 'FOREIGN SERVER'"
" UNION SELECT 'FUNCTION'" " UNION SELECT 'FUNCTION'"
" UNION SELECT 'LANGUAGE'" " UNION SELECT 'LANGUAGE'"
" UNION SELECT 'LARGE OBJECT'"
" UNION SELECT 'SCHEMA'" " UNION SELECT 'SCHEMA'"
" UNION SELECT 'TABLESPACE'"); " UNION SELECT 'TABLESPACE'");
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200912071 #define CATALOG_VERSION_NO 200912111
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -128,6 +128,7 @@ typedef enum ObjectClass ...@@ -128,6 +128,7 @@ typedef enum ObjectClass
OCLASS_CONVERSION, /* pg_conversion */ OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */ OCLASS_DEFAULT, /* pg_attrdef */
OCLASS_LANGUAGE, /* pg_language */ OCLASS_LANGUAGE, /* pg_language */
OCLASS_LARGEOBJECT, /* pg_largeobject */
OCLASS_OPERATOR, /* pg_operator */ OCLASS_OPERATOR, /* pg_operator */
OCLASS_OPCLASS, /* pg_opclass */ OCLASS_OPCLASS, /* pg_opclass */
OCLASS_OPFAMILY, /* pg_opfamily */ OCLASS_OPFAMILY, /* pg_opfamily */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -165,6 +165,9 @@ DECLARE_UNIQUE_INDEX(pg_language_oid_index, 2682, on pg_language using btree(oid ...@@ -165,6 +165,9 @@ DECLARE_UNIQUE_INDEX(pg_language_oid_index, 2682, on pg_language using btree(oid
DECLARE_UNIQUE_INDEX(pg_largeobject_loid_pn_index, 2683, on pg_largeobject using btree(loid oid_ops, pageno int4_ops)); DECLARE_UNIQUE_INDEX(pg_largeobject_loid_pn_index, 2683, on pg_largeobject using btree(loid oid_ops, pageno int4_ops));
#define LargeObjectLOidPNIndexId 2683 #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)); DECLARE_UNIQUE_INDEX(pg_namespace_nspname_index, 2684, on pg_namespace using btree(nspname name_ops));
#define NamespaceNameIndexId 2684 #define NamespaceNameIndexId 2684
DECLARE_UNIQUE_INDEX(pg_namespace_oid_index, 2685, on pg_namespace using btree(oid oid_ops)); DECLARE_UNIQUE_INDEX(pg_namespace_oid_index, 2685, on pg_namespace using btree(oid oid_ops));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -51,8 +51,9 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; ...@@ -51,8 +51,9 @@ typedef FormData_pg_largeobject *Form_pg_largeobject;
#define Anum_pg_largeobject_pageno 2 #define Anum_pg_largeobject_pageno 2
#define Anum_pg_largeobject_data 3 #define Anum_pg_largeobject_data 3
extern void LargeObjectCreate(Oid loid); extern Oid LargeObjectCreate(Oid loid);
extern void LargeObjectDrop(Oid loid); extern void LargeObjectDrop(Oid loid);
extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId);
extern bool LargeObjectExists(Oid loid); extern bool LargeObjectExists(Oid loid);
#endif /* PG_LARGEOBJECT_H */ #endif /* PG_LARGEOBJECT_H */
/*-------------------------------------------------------------------------
*
* 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 */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -37,6 +37,11 @@ extern Datum lo_tell(PG_FUNCTION_ARGS); ...@@ -37,6 +37,11 @@ extern Datum lo_tell(PG_FUNCTION_ARGS);
extern Datum lo_unlink(PG_FUNCTION_ARGS); extern Datum lo_unlink(PG_FUNCTION_ARGS);
extern Datum lo_truncate(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. * These are not fmgr-callable, but are available to C code.
* Probably these should have had the underscore-free names, * Probably these should have had the underscore-free names,
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1195,6 +1195,7 @@ typedef enum GrantObjectType ...@@ -1195,6 +1195,7 @@ typedef enum GrantObjectType
ACL_OBJECT_FOREIGN_SERVER, /* foreign server */ ACL_OBJECT_FOREIGN_SERVER, /* foreign server */
ACL_OBJECT_FUNCTION, /* function */ ACL_OBJECT_FUNCTION, /* function */
ACL_OBJECT_LANGUAGE, /* procedural language */ ACL_OBJECT_LANGUAGE, /* procedural language */
ACL_OBJECT_LARGEOBJECT, /* largeobject */
ACL_OBJECT_NAMESPACE, /* namespace */ ACL_OBJECT_NAMESPACE, /* namespace */
ACL_OBJECT_TABLESPACE /* tablespace */ ACL_OBJECT_TABLESPACE /* tablespace */
} GrantObjectType; } GrantObjectType;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* An ACL array is simply an array of AclItems, representing the union * An ACL array is simply an array of AclItems, representing the union
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "nodes/parsenodes.h" #include "nodes/parsenodes.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/snapshot.h"
/* /*
...@@ -151,6 +152,7 @@ typedef ArrayType Acl; ...@@ -151,6 +152,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE) #define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE)
#define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE) #define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE)
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE) #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_NAMESPACE (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE) #define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
...@@ -181,6 +183,7 @@ typedef enum AclObjectKind ...@@ -181,6 +183,7 @@ typedef enum AclObjectKind
ACL_KIND_OPER, /* pg_operator */ ACL_KIND_OPER, /* pg_operator */
ACL_KIND_TYPE, /* pg_type */ ACL_KIND_TYPE, /* pg_type */
ACL_KIND_LANGUAGE, /* pg_language */ ACL_KIND_LANGUAGE, /* pg_language */
ACL_KIND_LARGEOBJECT, /* pg_largeobject */
ACL_KIND_NAMESPACE, /* pg_namespace */ ACL_KIND_NAMESPACE, /* pg_namespace */
ACL_KIND_OPCLASS, /* pg_opclass */ ACL_KIND_OPCLASS, /* pg_opclass */
ACL_KIND_OPFAMILY, /* pg_opfamily */ ACL_KIND_OPFAMILY, /* pg_opfamily */
...@@ -259,6 +262,8 @@ extern AclMode pg_proc_aclmask(Oid proc_oid, Oid roleid, ...@@ -259,6 +262,8 @@ extern AclMode pg_proc_aclmask(Oid proc_oid, Oid roleid,
AclMode mask, AclMaskHow how); AclMode mask, AclMaskHow how);
extern AclMode pg_language_aclmask(Oid lang_oid, Oid roleid, extern AclMode pg_language_aclmask(Oid lang_oid, Oid roleid,
AclMode mask, AclMaskHow how); 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, extern AclMode pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
AclMode mask, AclMaskHow how); AclMode mask, AclMaskHow how);
extern AclMode pg_tablespace_aclmask(Oid spc_oid, Oid roleid, extern AclMode pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
...@@ -276,6 +281,8 @@ extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode); ...@@ -276,6 +281,8 @@ extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode);
extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode); 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_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode);
extern AclResult pg_language_aclcheck(Oid lang_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_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode);
extern AclResult pg_tablespace_aclcheck(Oid spc_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 AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
...@@ -293,6 +300,7 @@ extern bool pg_type_ownercheck(Oid type_oid, Oid roleid); ...@@ -293,6 +300,7 @@ extern bool pg_type_ownercheck(Oid type_oid, Oid roleid);
extern bool pg_oper_ownercheck(Oid oper_oid, Oid roleid); extern bool pg_oper_ownercheck(Oid oper_oid, Oid roleid);
extern bool pg_proc_ownercheck(Oid proc_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_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_namespace_ownercheck(Oid nsp_oid, Oid roleid);
extern bool pg_tablespace_ownercheck(Oid spc_oid, Oid roleid); extern bool pg_tablespace_ownercheck(Oid spc_oid, Oid roleid);
extern bool pg_opclass_ownercheck(Oid opc_oid, Oid roleid); extern bool pg_opclass_ownercheck(Oid opc_oid, Oid roleid);
......
...@@ -11,6 +11,12 @@ DROP ROLE IF EXISTS regressuser2; ...@@ -11,6 +11,12 @@ DROP ROLE IF EXISTS regressuser2;
DROP ROLE IF EXISTS regressuser3; DROP ROLE IF EXISTS regressuser3;
DROP ROLE IF EXISTS regressuser4; DROP ROLE IF EXISTS regressuser4;
DROP ROLE IF EXISTS regressuser5; 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; RESET client_min_messages;
-- test proper begins here -- test proper begins here
CREATE USER regressuser1; CREATE USER regressuser1;
...@@ -847,6 +853,194 @@ SELECT has_sequence_privilege('x_seq', 'USAGE'); ...@@ -847,6 +853,194 @@ SELECT has_sequence_privilege('x_seq', 'USAGE');
t t
(1 row) (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 -- test default ACLs
\c - \c -
CREATE SCHEMA testns; CREATE SCHEMA testns;
...@@ -1034,6 +1228,16 @@ DROP TABLE atest6; ...@@ -1034,6 +1228,16 @@ DROP TABLE atest6;
DROP TABLE atestc; DROP TABLE atestc;
DROP TABLE atestp1; DROP TABLE atestp1;
DROP TABLE atestp2; 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 regressgroup1;
DROP GROUP regressgroup2; DROP GROUP regressgroup2;
-- these are needed to clean up permissions -- these are needed to clean up permissions
...@@ -1044,3 +1248,5 @@ DROP USER regressuser2; ...@@ -1044,3 +1248,5 @@ DROP USER regressuser2;
DROP USER regressuser3; DROP USER regressuser3;
DROP USER regressuser4; DROP USER regressuser4;
DROP USER regressuser5; DROP USER regressuser5;
DROP USER regressuser6;
ERROR: role "regressuser6" does not exist
...@@ -106,6 +106,7 @@ SELECT relname, relhasindex ...@@ -106,6 +106,7 @@ SELECT relname, relhasindex
pg_inherits | t pg_inherits | t
pg_language | t pg_language | t
pg_largeobject | t pg_largeobject | t
pg_largeobject_metadata | t
pg_listener | f pg_listener | f
pg_namespace | t pg_namespace | t
pg_opclass | t pg_opclass | t
...@@ -153,7 +154,7 @@ SELECT relname, relhasindex ...@@ -153,7 +154,7 @@ SELECT relname, relhasindex
timetz_tbl | f timetz_tbl | f
tinterval_tbl | f tinterval_tbl | f
varchar_tbl | f varchar_tbl | f
(142 rows) (143 rows)
-- --
-- another sanity check: every system catalog that has OIDs should have -- another sanity check: every system catalog that has OIDs should have
......
...@@ -15,6 +15,9 @@ DROP ROLE IF EXISTS regressuser2; ...@@ -15,6 +15,9 @@ DROP ROLE IF EXISTS regressuser2;
DROP ROLE IF EXISTS regressuser3; DROP ROLE IF EXISTS regressuser3;
DROP ROLE IF EXISTS regressuser4; DROP ROLE IF EXISTS regressuser4;
DROP ROLE IF EXISTS regressuser5; DROP ROLE IF EXISTS regressuser5;
DROP ROLE IF EXISTS regressuser6;
SELECT lo_unlink(oid) FROM pg_largeobject_metadata;
RESET client_min_messages; RESET client_min_messages;
...@@ -36,7 +39,6 @@ ALTER GROUP regressgroup2 ADD USER regressuser2; -- duplicate ...@@ -36,7 +39,6 @@ ALTER GROUP regressgroup2 ADD USER regressuser2; -- duplicate
ALTER GROUP regressgroup2 DROP USER regressuser2; ALTER GROUP regressgroup2 DROP USER regressuser2;
ALTER GROUP regressgroup2 ADD USER regressuser4; ALTER GROUP regressgroup2 ADD USER regressuser4;
-- test owner privileges -- test owner privileges
SET SESSION AUTHORIZATION regressuser1; SET SESSION AUTHORIZATION regressuser1;
...@@ -485,6 +487,83 @@ SET SESSION AUTHORIZATION regressuser2; ...@@ -485,6 +487,83 @@ SET SESSION AUTHORIZATION regressuser2;
SELECT has_sequence_privilege('x_seq', 'USAGE'); 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 -- test default ACLs
\c - \c -
...@@ -611,6 +690,8 @@ DROP TABLE atestc; ...@@ -611,6 +690,8 @@ DROP TABLE atestc;
DROP TABLE atestp1; DROP TABLE atestp1;
DROP TABLE atestp2; DROP TABLE atestp2;
SELECT lo_unlink(oid) FROM pg_largeobject_metadata;
DROP GROUP regressgroup1; DROP GROUP regressgroup1;
DROP GROUP regressgroup2; DROP GROUP regressgroup2;
...@@ -623,3 +704,4 @@ DROP USER regressuser2; ...@@ -623,3 +704,4 @@ DROP USER regressuser2;
DROP USER regressuser3; DROP USER regressuser3;
DROP USER regressuser4; DROP USER regressuser4;
DROP USER regressuser5; DROP USER regressuser5;
DROP USER regressuser6;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment