mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
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.
This commit is contained in:
parent
64579962bb
commit
f1325ce213
@ -1,4 +1,4 @@
|
|||||||
<!-- $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 @@
|
|||||||
|
|
||||||
<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 @@
|
|||||||
|
|
||||||
<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 @@
|
|||||||
<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 @@
|
|||||||
<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 @@
|
|||||||
|
|
||||||
</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>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $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'
|
|||||||
</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>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $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
|
|||||||
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">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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.
|
|||||||
<!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">
|
||||||
|
75
doc/src/sgml/ref/alter_large_object.sgml
Executable file
75
doc/src/sgml/ref/alter_large_object.sgml
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
<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>
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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 ] }
|
|||||||
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
|
|||||||
<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
|
|||||||
<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>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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 ]
|
|||||||
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> [, ...]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $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 @@
|
|||||||
&alterGroup;
|
&alterGroup;
|
||||||
&alterIndex;
|
&alterIndex;
|
||||||
&alterLanguage;
|
&alterLanguage;
|
||||||
|
&alterLargeObject;
|
||||||
&alterOperator;
|
&alterOperator;
|
||||||
&alterOperatorClass;
|
&alterOperatorClass;
|
||||||
&alterOperatorFamily;
|
&alterOperatorFamily;
|
||||||
|
@ -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/,\
|
|||||||
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 \
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.156 2009/10/12 20:39:39 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.157 2009/12/11 03:34:55 itagaki Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* See acl.h.
|
* See acl.h.
|
||||||
@ -31,6 +31,8 @@
|
|||||||
#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_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"
|
||||||
@ -103,6 +105,7 @@ static void ExecGrant_Fdw(InternalGrant *grantStmt);
|
|||||||
static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
|
static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
|
||||||
static void ExecGrant_Function(InternalGrant *grantStmt);
|
static void ExecGrant_Function(InternalGrant *grantStmt);
|
||||||
static void ExecGrant_Language(InternalGrant *grantStmt);
|
static void ExecGrant_Language(InternalGrant *grantStmt);
|
||||||
|
static void ExecGrant_Largeobject(InternalGrant *grantStmt);
|
||||||
static void ExecGrant_Namespace(InternalGrant *grantStmt);
|
static void ExecGrant_Namespace(InternalGrant *grantStmt);
|
||||||
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
|
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
|
||||||
|
|
||||||
@ -251,6 +254,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
|
|||||||
case ACL_KIND_LANGUAGE:
|
case ACL_KIND_LANGUAGE:
|
||||||
whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
|
whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
|
||||||
break;
|
break;
|
||||||
|
case ACL_KIND_LARGEOBJECT:
|
||||||
|
whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT;
|
||||||
|
break;
|
||||||
case ACL_KIND_NAMESPACE:
|
case ACL_KIND_NAMESPACE:
|
||||||
whole_mask = ACL_ALL_RIGHTS_NAMESPACE;
|
whole_mask = ACL_ALL_RIGHTS_NAMESPACE;
|
||||||
break;
|
break;
|
||||||
@ -410,6 +416,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
|
|||||||
all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
|
all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
|
||||||
errormsg = gettext_noop("invalid privilege type %s for language");
|
errormsg = gettext_noop("invalid privilege type %s for language");
|
||||||
break;
|
break;
|
||||||
|
case ACL_OBJECT_LARGEOBJECT:
|
||||||
|
all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
|
||||||
|
errormsg = gettext_noop("invalid privilege type %s for large object");
|
||||||
|
break;
|
||||||
case ACL_OBJECT_NAMESPACE:
|
case ACL_OBJECT_NAMESPACE:
|
||||||
all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
|
all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
|
||||||
errormsg = gettext_noop("invalid privilege type %s for schema");
|
errormsg = gettext_noop("invalid privilege type %s for schema");
|
||||||
@ -513,6 +523,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
|
|||||||
case ACL_OBJECT_LANGUAGE:
|
case ACL_OBJECT_LANGUAGE:
|
||||||
ExecGrant_Language(istmt);
|
ExecGrant_Language(istmt);
|
||||||
break;
|
break;
|
||||||
|
case ACL_OBJECT_LARGEOBJECT:
|
||||||
|
ExecGrant_Largeobject(istmt);
|
||||||
|
break;
|
||||||
case ACL_OBJECT_NAMESPACE:
|
case ACL_OBJECT_NAMESPACE:
|
||||||
ExecGrant_Namespace(istmt);
|
ExecGrant_Namespace(istmt);
|
||||||
break;
|
break;
|
||||||
@ -597,6 +610,20 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
|
|||||||
ReleaseSysCache(tuple);
|
ReleaseSysCache(tuple);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ACL_OBJECT_LARGEOBJECT:
|
||||||
|
foreach(cell, objnames)
|
||||||
|
{
|
||||||
|
Oid lobjOid = intVal(lfirst(cell));
|
||||||
|
|
||||||
|
if (!LargeObjectExists(lobjOid))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("large object %u does not exist",
|
||||||
|
lobjOid)));
|
||||||
|
|
||||||
|
objects = lappend_oid(objects, lobjOid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ACL_OBJECT_NAMESPACE:
|
case ACL_OBJECT_NAMESPACE:
|
||||||
foreach(cell, objnames)
|
foreach(cell, objnames)
|
||||||
{
|
{
|
||||||
@ -1279,6 +1306,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
|
|||||||
case LanguageRelationId:
|
case LanguageRelationId:
|
||||||
istmt.objtype = ACL_OBJECT_LANGUAGE;
|
istmt.objtype = ACL_OBJECT_LANGUAGE;
|
||||||
break;
|
break;
|
||||||
|
case LargeObjectRelationId:
|
||||||
|
istmt.objtype = ACL_OBJECT_LARGEOBJECT;
|
||||||
|
break;
|
||||||
case NamespaceRelationId:
|
case NamespaceRelationId:
|
||||||
istmt.objtype = ACL_OBJECT_NAMESPACE;
|
istmt.objtype = ACL_OBJECT_NAMESPACE;
|
||||||
break;
|
break;
|
||||||
@ -2472,6 +2502,138 @@ ExecGrant_Language(InternalGrant *istmt)
|
|||||||
heap_close(relation, RowExclusiveLock);
|
heap_close(relation, RowExclusiveLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ExecGrant_Largeobject(InternalGrant *istmt)
|
||||||
|
{
|
||||||
|
Relation relation;
|
||||||
|
ListCell *cell;
|
||||||
|
|
||||||
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
||||||
|
istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
|
||||||
|
|
||||||
|
relation = heap_open(LargeObjectMetadataRelationId,
|
||||||
|
RowExclusiveLock);
|
||||||
|
|
||||||
|
foreach(cell, istmt->objects)
|
||||||
|
{
|
||||||
|
Oid loid = lfirst_oid(cell);
|
||||||
|
Form_pg_largeobject_metadata form_lo_meta;
|
||||||
|
char loname[NAMEDATALEN];
|
||||||
|
Datum aclDatum;
|
||||||
|
bool isNull;
|
||||||
|
AclMode avail_goptions;
|
||||||
|
AclMode this_privileges;
|
||||||
|
Acl *old_acl;
|
||||||
|
Acl *new_acl;
|
||||||
|
Oid grantorId;
|
||||||
|
Oid ownerId;
|
||||||
|
HeapTuple newtuple;
|
||||||
|
Datum values[Natts_pg_largeobject_metadata];
|
||||||
|
bool nulls[Natts_pg_largeobject_metadata];
|
||||||
|
bool replaces[Natts_pg_largeobject_metadata];
|
||||||
|
int noldmembers;
|
||||||
|
int nnewmembers;
|
||||||
|
Oid *oldmembers;
|
||||||
|
Oid *newmembers;
|
||||||
|
ScanKeyData entry[1];
|
||||||
|
SysScanDesc scan;
|
||||||
|
HeapTuple tuple;
|
||||||
|
|
||||||
|
/* There's no syscache for pg_largeobject_metadata */
|
||||||
|
ScanKeyInit(&entry[0],
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(loid));
|
||||||
|
|
||||||
|
scan = systable_beginscan(relation,
|
||||||
|
LargeObjectMetadataOidIndexId, true,
|
||||||
|
SnapshotNow, 1, entry);
|
||||||
|
|
||||||
|
tuple = systable_getnext(scan);
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
elog(ERROR, "cache lookup failed for large object %u", loid);
|
||||||
|
|
||||||
|
form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
||||||
|
* substitute the proper default.
|
||||||
|
*/
|
||||||
|
ownerId = form_lo_meta->lomowner;
|
||||||
|
aclDatum = heap_getattr(tuple,
|
||||||
|
Anum_pg_largeobject_metadata_lomacl,
|
||||||
|
RelationGetDescr(relation), &isNull);
|
||||||
|
if (isNull)
|
||||||
|
old_acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId);
|
||||||
|
else
|
||||||
|
old_acl = DatumGetAclPCopy(aclDatum);
|
||||||
|
|
||||||
|
/* Determine ID to do the grant as, and available grant options */
|
||||||
|
select_best_grantor(GetUserId(), istmt->privileges,
|
||||||
|
old_acl, ownerId,
|
||||||
|
&grantorId, &avail_goptions);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restrict the privileges to what we can actually grant, and emit the
|
||||||
|
* standards-mandated warning and error messages.
|
||||||
|
*/
|
||||||
|
snprintf(loname, sizeof(loname), "large object %u", loid);
|
||||||
|
this_privileges =
|
||||||
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
||||||
|
istmt->all_privs, istmt->privileges,
|
||||||
|
loid, grantorId, ACL_KIND_LARGEOBJECT,
|
||||||
|
loname, 0, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate new ACL.
|
||||||
|
*
|
||||||
|
* We need the members of both old and new ACLs so we can correct the
|
||||||
|
* shared dependency information.
|
||||||
|
*/
|
||||||
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
||||||
|
|
||||||
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
||||||
|
istmt->grant_option, istmt->behavior,
|
||||||
|
istmt->grantees, this_privileges,
|
||||||
|
grantorId, ownerId);
|
||||||
|
|
||||||
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
||||||
|
|
||||||
|
/* finished building new ACL value, now insert it */
|
||||||
|
MemSet(values, 0, sizeof(values));
|
||||||
|
MemSet(nulls, false, sizeof(nulls));
|
||||||
|
MemSet(replaces, false, sizeof(replaces));
|
||||||
|
|
||||||
|
replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
|
||||||
|
values[Anum_pg_largeobject_metadata_lomacl - 1]
|
||||||
|
= PointerGetDatum(new_acl);
|
||||||
|
|
||||||
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
|
||||||
|
values, nulls, replaces);
|
||||||
|
|
||||||
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
||||||
|
|
||||||
|
/* keep the catalog indexes up to date */
|
||||||
|
CatalogUpdateIndexes(relation, newtuple);
|
||||||
|
|
||||||
|
/* Update the shared dependency ACL info */
|
||||||
|
updateAclDependencies(LargeObjectRelationId,
|
||||||
|
HeapTupleGetOid(tuple), 0,
|
||||||
|
ownerId, istmt->is_grant,
|
||||||
|
noldmembers, oldmembers,
|
||||||
|
nnewmembers, newmembers);
|
||||||
|
|
||||||
|
systable_endscan(scan);
|
||||||
|
|
||||||
|
pfree(new_acl);
|
||||||
|
|
||||||
|
/* prevent error when processing duplicate objects */
|
||||||
|
CommandCounterIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_close(relation, RowExclusiveLock);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ExecGrant_Namespace(InternalGrant *istmt)
|
ExecGrant_Namespace(InternalGrant *istmt)
|
||||||
{
|
{
|
||||||
@ -2812,6 +2974,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
|
|||||||
gettext_noop("permission denied for type %s"),
|
gettext_noop("permission denied for type %s"),
|
||||||
/* ACL_KIND_LANGUAGE */
|
/* ACL_KIND_LANGUAGE */
|
||||||
gettext_noop("permission denied for language %s"),
|
gettext_noop("permission denied for language %s"),
|
||||||
|
/* ACL_KIND_LARGEOBJECT */
|
||||||
|
gettext_noop("permission denied for large object %s"),
|
||||||
/* ACL_KIND_NAMESPACE */
|
/* ACL_KIND_NAMESPACE */
|
||||||
gettext_noop("permission denied for schema %s"),
|
gettext_noop("permission denied for schema %s"),
|
||||||
/* ACL_KIND_OPCLASS */
|
/* ACL_KIND_OPCLASS */
|
||||||
@ -2850,6 +3014,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
|
|||||||
gettext_noop("must be owner of type %s"),
|
gettext_noop("must be owner of type %s"),
|
||||||
/* ACL_KIND_LANGUAGE */
|
/* ACL_KIND_LANGUAGE */
|
||||||
gettext_noop("must be owner of language %s"),
|
gettext_noop("must be owner of language %s"),
|
||||||
|
/* ACL_KIND_LARGEOBJECT */
|
||||||
|
gettext_noop("must be owner of large object %s"),
|
||||||
/* ACL_KIND_NAMESPACE */
|
/* ACL_KIND_NAMESPACE */
|
||||||
gettext_noop("must be owner of schema %s"),
|
gettext_noop("must be owner of schema %s"),
|
||||||
/* ACL_KIND_OPCLASS */
|
/* ACL_KIND_OPCLASS */
|
||||||
@ -2969,6 +3135,9 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid,
|
|||||||
return pg_proc_aclmask(table_oid, roleid, mask, how);
|
return pg_proc_aclmask(table_oid, roleid, mask, how);
|
||||||
case ACL_KIND_LANGUAGE:
|
case ACL_KIND_LANGUAGE:
|
||||||
return pg_language_aclmask(table_oid, roleid, mask, how);
|
return pg_language_aclmask(table_oid, roleid, mask, how);
|
||||||
|
case ACL_KIND_LARGEOBJECT:
|
||||||
|
return pg_largeobject_aclmask_snapshot(table_oid, roleid,
|
||||||
|
mask, how, SnapshotNow);
|
||||||
case ACL_KIND_NAMESPACE:
|
case ACL_KIND_NAMESPACE:
|
||||||
return pg_namespace_aclmask(table_oid, roleid, mask, how);
|
return pg_namespace_aclmask(table_oid, roleid, mask, how);
|
||||||
case ACL_KIND_TABLESPACE:
|
case ACL_KIND_TABLESPACE:
|
||||||
@ -3351,6 +3520,90 @@ pg_language_aclmask(Oid lang_oid, Oid roleid,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exported routine for examining a user's privileges for a largeobject
|
||||||
|
*
|
||||||
|
* The reason why this interface has an argument of snapshot is that
|
||||||
|
* we apply a snapshot available on lo_open(), not SnapshotNow, when
|
||||||
|
* it is opened as read-only mode.
|
||||||
|
* If we could see the metadata and data from inconsistent viewpoint,
|
||||||
|
* it will give us much confusion. So, we need to provide an interface
|
||||||
|
* which takes an argument of snapshot.
|
||||||
|
*
|
||||||
|
* If the caller refers a large object with a certain snapshot except
|
||||||
|
* for SnapshotNow, its permission checks should be also applied in
|
||||||
|
* the same snapshot.
|
||||||
|
*/
|
||||||
|
AclMode
|
||||||
|
pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
|
||||||
|
AclMode mask, AclMaskHow how,
|
||||||
|
Snapshot snapshot)
|
||||||
|
{
|
||||||
|
AclMode result;
|
||||||
|
Relation pg_lo_meta;
|
||||||
|
ScanKeyData entry[1];
|
||||||
|
SysScanDesc scan;
|
||||||
|
HeapTuple tuple;
|
||||||
|
Datum aclDatum;
|
||||||
|
bool isNull;
|
||||||
|
Acl *acl;
|
||||||
|
Oid ownerId;
|
||||||
|
|
||||||
|
/* Superusers bypass all permission checking. */
|
||||||
|
if (superuser_arg(roleid))
|
||||||
|
return mask;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the largeobject's ACL from pg_language_metadata
|
||||||
|
*/
|
||||||
|
pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
||||||
|
AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&entry[0],
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(lobj_oid));
|
||||||
|
|
||||||
|
scan = systable_beginscan(pg_lo_meta,
|
||||||
|
LargeObjectMetadataOidIndexId, true,
|
||||||
|
snapshot, 1, entry);
|
||||||
|
|
||||||
|
tuple = systable_getnext(scan);
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("large object %u does not exist", lobj_oid)));
|
||||||
|
|
||||||
|
ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
|
||||||
|
|
||||||
|
aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl,
|
||||||
|
RelationGetDescr(pg_lo_meta), &isNull);
|
||||||
|
|
||||||
|
if (isNull)
|
||||||
|
{
|
||||||
|
/* No ACL, so build default ACL */
|
||||||
|
acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId);
|
||||||
|
aclDatum = (Datum) 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* detoast ACL if necessary */
|
||||||
|
acl = DatumGetAclP(aclDatum);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
||||||
|
|
||||||
|
/* if we have a detoasted copy, free it */
|
||||||
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
||||||
|
pfree(acl);
|
||||||
|
|
||||||
|
systable_endscan(scan);
|
||||||
|
|
||||||
|
heap_close(pg_lo_meta, AccessShareLock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Exported routine for examining a user's privileges for a namespace
|
* Exported routine for examining a user's privileges for a namespace
|
||||||
*/
|
*/
|
||||||
@ -3801,6 +4054,20 @@ pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode)
|
|||||||
return ACLCHECK_NO_PRIV;
|
return ACLCHECK_NO_PRIV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exported routine for checking a user's access privileges to a largeobject
|
||||||
|
*/
|
||||||
|
AclResult
|
||||||
|
pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode,
|
||||||
|
Snapshot snapshot)
|
||||||
|
{
|
||||||
|
if (pg_largeobject_aclmask_snapshot(lobj_oid, roleid, mode,
|
||||||
|
ACLMASK_ANY, snapshot) != 0)
|
||||||
|
return ACLCHECK_OK;
|
||||||
|
else
|
||||||
|
return ACLCHECK_NO_PRIV;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Exported routine for checking a user's access privileges to a namespace
|
* Exported routine for checking a user's access privileges to a namespace
|
||||||
*/
|
*/
|
||||||
@ -3991,6 +4258,53 @@ pg_language_ownercheck(Oid lan_oid, Oid roleid)
|
|||||||
return has_privs_of_role(roleid, ownerId);
|
return has_privs_of_role(roleid, ownerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ownership check for a largeobject (specified by OID)
|
||||||
|
*
|
||||||
|
* Note that we have no candidate to call this routine with a certain
|
||||||
|
* snapshot except for SnapshotNow, so we don't provide an interface
|
||||||
|
* with _snapshot() version now.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid)
|
||||||
|
{
|
||||||
|
Relation pg_lo_meta;
|
||||||
|
ScanKeyData entry[1];
|
||||||
|
SysScanDesc scan;
|
||||||
|
HeapTuple tuple;
|
||||||
|
Oid ownerId;
|
||||||
|
|
||||||
|
/* Superusers bypass all permission checking. */
|
||||||
|
if (superuser_arg(roleid))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* There's no syscache for pg_largeobject_metadata */
|
||||||
|
pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
||||||
|
AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&entry[0],
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(lobj_oid));
|
||||||
|
|
||||||
|
scan = systable_beginscan(pg_lo_meta,
|
||||||
|
LargeObjectMetadataOidIndexId, true,
|
||||||
|
SnapshotNow, 1, entry);
|
||||||
|
|
||||||
|
tuple = systable_getnext(scan);
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("large object %u does not exist", lobj_oid)));
|
||||||
|
|
||||||
|
ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
|
||||||
|
|
||||||
|
systable_endscan(scan);
|
||||||
|
heap_close(pg_lo_meta, AccessShareLock);
|
||||||
|
|
||||||
|
return has_privs_of_role(roleid, ownerId);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ownership check for a namespace (specified by OID).
|
* Ownership check for a namespace (specified by OID).
|
||||||
*/
|
*/
|
||||||
|
@ -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 @@
|
|||||||
#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] = {
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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 @@
|
|||||||
|
|
||||||
#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 @@
|
|||||||
/*
|
/*
|
||||||
* 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,
|
||||||
|
SnapshotNow, 1, skey);
|
||||||
|
|
||||||
sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
|
tuple = systable_getnext(scan);
|
||||||
SnapshotNow, 1, skey);
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
|
||||||
while ((tuple = systable_getnext(sd)) != NULL)
|
|
||||||
{
|
|
||||||
simple_heap_delete(pg_largeobject, &tuple->t_self);
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
systable_endscan(sd);
|
|
||||||
|
|
||||||
heap_close(pg_largeobject, RowExclusiveLock);
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
ereport(ERROR,
|
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)));
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
simple_heap_delete(pg_lo_meta, &tuple->t_self);
|
||||||
LargeObjectExists(Oid loid)
|
|
||||||
{
|
systable_endscan(scan);
|
||||||
bool retval = false;
|
|
||||||
Relation pg_largeobject;
|
|
||||||
ScanKeyData skey[1];
|
|
||||||
SysScanDesc sd;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See if we can find any tuples belonging to the specified LO
|
* Delete all the associated entries from pg_largeobject
|
||||||
*/
|
*/
|
||||||
ScanKeyInit(&skey[0],
|
ScanKeyInit(&skey[0],
|
||||||
Anum_pg_largeobject_loid,
|
Anum_pg_largeobject_loid,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(loid));
|
ObjectIdGetDatum(loid));
|
||||||
|
|
||||||
pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock);
|
scan = systable_beginscan(pg_largeobject,
|
||||||
|
LargeObjectLOidPNIndexId, true,
|
||||||
|
SnapshotNow, 1, skey);
|
||||||
|
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
|
||||||
|
{
|
||||||
|
simple_heap_delete(pg_largeobject, &tuple->t_self);
|
||||||
|
}
|
||||||
|
|
||||||
sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true,
|
systable_endscan(scan);
|
||||||
|
|
||||||
|
heap_close(pg_largeobject, RowExclusiveLock);
|
||||||
|
|
||||||
|
heap_close(pg_lo_meta, RowExclusiveLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LargeObjectAlterOwner
|
||||||
|
*
|
||||||
|
* Implementation of ALTER LARGE OBJECT statement
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
LargeObjectAlterOwner(Oid loid, Oid newOwnerId)
|
||||||
|
{
|
||||||
|
Form_pg_largeobject_metadata form_lo_meta;
|
||||||
|
Relation pg_lo_meta;
|
||||||
|
ScanKeyData skey[1];
|
||||||
|
SysScanDesc scan;
|
||||||
|
HeapTuple oldtup;
|
||||||
|
HeapTuple newtup;
|
||||||
|
|
||||||
|
pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
||||||
|
RowExclusiveLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&skey[0],
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(loid));
|
||||||
|
|
||||||
|
scan = systable_beginscan(pg_lo_meta,
|
||||||
|
LargeObjectMetadataOidIndexId, true,
|
||||||
|
SnapshotNow, 1, skey);
|
||||||
|
|
||||||
|
oldtup = systable_getnext(scan);
|
||||||
|
if (!HeapTupleIsValid(oldtup))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("large object %u does not exist", loid)));
|
||||||
|
|
||||||
|
form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(oldtup);
|
||||||
|
if (form_lo_meta->lomowner != newOwnerId)
|
||||||
|
{
|
||||||
|
Datum values[Natts_pg_largeobject_metadata];
|
||||||
|
bool nulls[Natts_pg_largeobject_metadata];
|
||||||
|
bool replaces[Natts_pg_largeobject_metadata];
|
||||||
|
Acl *newAcl;
|
||||||
|
Datum aclDatum;
|
||||||
|
bool isnull;
|
||||||
|
|
||||||
|
/* Superusers can always do it */
|
||||||
|
if (!superuser())
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The 'lo_compat_privileges' is not checked here, because we
|
||||||
|
* don't have any access control features in the 8.4.x series
|
||||||
|
* or earlier release.
|
||||||
|
* So, it is not a place we can define a compatible behavior.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Otherwise, must be owner of the existing object */
|
||||||
|
if (!pg_largeobject_ownercheck(loid, GetUserId()))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("must be owner of large object %u", loid)));
|
||||||
|
|
||||||
|
/* Must be able to become new owner */
|
||||||
|
check_is_member_of_role(GetUserId(), newOwnerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(values, 0, sizeof(values));
|
||||||
|
memset(nulls, false, sizeof(nulls));
|
||||||
|
memset(replaces, false, sizeof(nulls));
|
||||||
|
|
||||||
|
values[Anum_pg_largeobject_metadata_lomowner - 1]
|
||||||
|
= ObjectIdGetDatum(newOwnerId);
|
||||||
|
replaces[Anum_pg_largeobject_metadata_lomowner - 1] = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine the modified ACL for the new owner.
|
||||||
|
* This is only necessary when the ACL is non-null.
|
||||||
|
*/
|
||||||
|
aclDatum = heap_getattr(oldtup,
|
||||||
|
Anum_pg_largeobject_metadata_lomacl,
|
||||||
|
RelationGetDescr(pg_lo_meta), &isnull);
|
||||||
|
if (!isnull)
|
||||||
|
{
|
||||||
|
newAcl = aclnewowner(DatumGetAclP(aclDatum),
|
||||||
|
form_lo_meta->lomowner, newOwnerId);
|
||||||
|
values[Anum_pg_largeobject_metadata_lomacl - 1]
|
||||||
|
= PointerGetDatum(newAcl);
|
||||||
|
replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_lo_meta),
|
||||||
|
values, nulls, replaces);
|
||||||
|
|
||||||
|
simple_heap_update(pg_lo_meta, &newtup->t_self, newtup);
|
||||||
|
CatalogUpdateIndexes(pg_lo_meta, newtup);
|
||||||
|
|
||||||
|
heap_freetuple(newtup);
|
||||||
|
|
||||||
|
/* Update owner dependency reference */
|
||||||
|
changeDependencyOnOwner(LargeObjectRelationId,
|
||||||
|
loid, newOwnerId);
|
||||||
|
}
|
||||||
|
systable_endscan(scan);
|
||||||
|
|
||||||
|
heap_close(pg_lo_meta, RowExclusiveLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LargeObjectExists
|
||||||
|
*
|
||||||
|
* Currently, we don't use system cache to contain metadata of
|
||||||
|
* large objects, because massive number of large objects can
|
||||||
|
* consume not a small amount of process local memory.
|
||||||
|
*
|
||||||
|
* Note that LargeObjectExists always scans the system catalog
|
||||||
|
* with SnapshotNow, so it is unavailable to use to check
|
||||||
|
* existence in read-only accesses.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
LargeObjectExists(Oid loid)
|
||||||
|
{
|
||||||
|
Relation pg_lo_meta;
|
||||||
|
ScanKeyData skey[1];
|
||||||
|
SysScanDesc sd;
|
||||||
|
HeapTuple tuple;
|
||||||
|
bool retval = false;
|
||||||
|
|
||||||
|
ScanKeyInit(&skey[0],
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(loid));
|
||||||
|
|
||||||
|
pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
||||||
|
AccessShareLock);
|
||||||
|
|
||||||
|
sd = systable_beginscan(pg_lo_meta,
|
||||||
|
LargeObjectMetadataOidIndexId, true,
|
||||||
SnapshotNow, 1, skey);
|
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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 @@
|
|||||||
#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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
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 @@
|
|||||||
* 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 @@
|
|||||||
#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 @@
|
|||||||
#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)
|
|||||||
(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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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 @@
|
|||||||
#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)
|
|||||||
(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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
(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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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);
|
|||||||
%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:
|
|||||||
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
|
|||||||
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; }
|
|||||||
| '-' 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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 @@
|
|||||||
|
|
||||||
#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)
|
|||||||
static bool
|
static bool
|
||||||
myLargeObjectExists(Oid loid, Snapshot snapshot)
|
myLargeObjectExists(Oid loid, Snapshot snapshot)
|
||||||
{
|
{
|
||||||
|
Relation pg_lo_meta;
|
||||||
|
ScanKeyData skey[1];
|
||||||
|
SysScanDesc sd;
|
||||||
|
HeapTuple tuple;
|
||||||
bool retval = false;
|
bool retval = false;
|
||||||
Relation pg_largeobject;
|
|
||||||
ScanKeyData skey[1];
|
|
||||||
SysScanDesc sd;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* See if we can find any tuples belonging to the specified LO
|
|
||||||
*/
|
|
||||||
ScanKeyInit(&skey[0],
|
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
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 @@
|
|||||||
* 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 @@
|
|||||||
#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[] =
|
|||||||
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 @@
|
|||||||
#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 @@
|
|||||||
* 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)
|
|||||||
" 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 @@
|
|||||||
* 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 { \
|
|||||||
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 @@
|
|||||||
* 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)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
" 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)
|
|||||||
/* 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");
|
||||||
|
}
|
||||||
|
|
||||||
printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ",
|
/* dump blob ownership, if necessary */
|
||||||
blobOid);
|
if (!PQgetisnull(res, i, 2))
|
||||||
appendStringLiteralAH(commentcmd, comment, AH);
|
{
|
||||||
appendPQExpBuffer(commentcmd, ";\n");
|
appendPQExpBuffer(cmdQry,
|
||||||
|
"ALTER LARGE OBJECT %u OWNER TO %s;\n",
|
||||||
|
blobOid, lo_owner);
|
||||||
|
}
|
||||||
|
|
||||||
archputs(commentcmd->data, AH);
|
/* dump blob privileges, if necessary */
|
||||||
|
if (!PQgetisnull(res, i, 3) &&
|
||||||
|
!dataOnly && !aclsSkip)
|
||||||
|
{
|
||||||
|
snprintf(lo_name, sizeof(lo_name), "%u", blobOid);
|
||||||
|
if (!buildACLCommands(lo_name, NULL, "LARGE OBJECT",
|
||||||
|
lo_acl, lo_owner, "",
|
||||||
|
AH->remoteVersion, cmdQry))
|
||||||
|
{
|
||||||
|
write_msg(NULL, "could not parse ACL (%s) for "
|
||||||
|
"large object %u", lo_acl, blobOid);
|
||||||
|
exit_nicely();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmdQry->len > 0)
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(cmdQry, "\n");
|
||||||
|
archputs(cmdQry->data, AH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (PQntuples(res) > 0);
|
} while (PQntuples(res) > 0);
|
||||||
|
|
||||||
@ -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 @@
|
|||||||
*
|
*
|
||||||
* 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,13 +278,28 @@ do_lo_list(void)
|
|||||||
char buf[1024];
|
char buf[1024];
|
||||||
printQueryOpt myopt = pset.popt;
|
printQueryOpt myopt = pset.popt;
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf),
|
if (pset.sversion >= 80500)
|
||||||
"SELECT loid as \"%s\",\n"
|
{
|
||||||
" pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
|
snprintf(buf, sizeof(buf),
|
||||||
"FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
|
"SELECT oid as \"%s\",\n"
|
||||||
"ORDER BY 1",
|
" pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n"
|
||||||
gettext_noop("ID"),
|
" pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
|
||||||
gettext_noop("Description"));
|
" FROM pg_catalog.pg_largeobject_metadata "
|
||||||
|
" ORDER BY oid",
|
||||||
|
gettext_noop("ID"),
|
||||||
|
gettext_noop("Owner"),
|
||||||
|
gettext_noop("Description"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(buf, sizeof(buf),
|
||||||
|
"SELECT loid as \"%s\",\n"
|
||||||
|
" pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
|
||||||
|
"FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
|
||||||
|
"ORDER BY 1",
|
||||||
|
gettext_noop("ID"),
|
||||||
|
gettext_noop("Description"));
|
||||||
|
}
|
||||||
|
|
||||||
res = PSQLexec(buf, false);
|
res = PSQLexec(buf, false);
|
||||||
if (!res)
|
if (!res)
|
||||||
|
@ -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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
" 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 @@
|
|||||||
* 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 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200912071
|
#define CATALOG_VERSION_NO 200912111
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -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
|
|||||||
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 @@
|
|||||||
* 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
|
|||||||
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 @@
|
|||||||
* 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;
|
|||||||
#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 */
|
||||||
|
52
src/include/catalog/pg_largeobject_metadata.h
Executable file
52
src/include/catalog/pg_largeobject_metadata.h
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* pg_largeobject_metadata.h
|
||||||
|
* definition of the system "largeobject_metadata" relation (pg_largeobject_metadata)
|
||||||
|
* along with the relation's initial contents.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* $PostgreSQL: 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 @@
|
|||||||
* 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);
|
|||||||
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 @@
|
|||||||
* 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
|
|||||||
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 @@
|
|||||||
* 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 @@
|
|||||||
|
|
||||||
#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;
|
|||||||
#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
|
|||||||
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,
|
|||||||
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);
|
|||||||
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);
|
|||||||
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;
|
|||||||
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');
|
|||||||
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;
|
|||||||
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;
|
|||||||
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
|
|||||||
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
|
|||||||
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;
|
|||||||
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
|
|||||||
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;
|
|||||||
|
|
||||||
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;
|
|||||||
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;
|
|||||||
DROP USER regressuser3;
|
DROP USER regressuser3;
|
||||||
DROP USER regressuser4;
|
DROP USER regressuser4;
|
||||||
DROP USER regressuser5;
|
DROP USER regressuser5;
|
||||||
|
DROP USER regressuser6;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user