Commit d9572c4e authored by Tom Lane's avatar Tom Lane

Core support for "extensions", which are packages of SQL objects.

This patch adds the server infrastructure to support extensions.
There is still one significant loose end, namely how to make it play nice
with pg_upgrade, so I am not yet committing the changes that would make
all the contrib modules depend on this feature.

In passing, fix a disturbingly large amount of breakage in
AlterObjectNamespace() and callers.

Dimitri Fontaine, reviewed by Anssi Kääriäinen,
Itagaki Takahiro, Tom Lane, and numerous others
parent 414c5a2e
...@@ -485,7 +485,7 @@ ...@@ -485,7 +485,7 @@
<term><acronym>PGXS</acronym></term> <term><acronym>PGXS</acronym></term>
<listitem> <listitem>
<para> <para>
<link linkend="xfunc-c-pgxs"><productname>PostgreSQL</> Extension System</link> <link linkend="extend-pgxs"><productname>PostgreSQL</> Extension System</link>
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -143,6 +143,11 @@ ...@@ -143,6 +143,11 @@
<entry>enum label and value definitions</entry> <entry>enum label and value definitions</entry>
</row> </row>
<row>
<entry><link linkend="catalog-pg-extension"><structname>pg_extension</structname></link></entry>
<entry>installed extensions</entry>
</row>
<row> <row>
<entry><link linkend="catalog-pg-foreign-data-wrapper"><structname>pg_foreign_data_wrapper</structname></link></entry> <entry><link linkend="catalog-pg-foreign-data-wrapper"><structname>pg_foreign_data_wrapper</structname></link></entry>
<entry>foreign-data wrapper definitions</entry> <entry>foreign-data wrapper definitions</entry>
...@@ -2679,6 +2684,21 @@ ...@@ -2679,6 +2684,21 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><symbol>DEPENDENCY_EXTENSION</> (<literal>e</>)</term>
<listitem>
<para>
The dependent object is a member of the <firstterm>extension</> that is
the referenced object (see
<link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>).
The dependent object can be dropped only via
<command>DROP EXTENSION</> on the referenced object. Functionally
this dependency type acts the same as an internal dependency, but
it's kept separate for clarity and to simplify <application>pg_dump</>.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><symbol>DEPENDENCY_PIN</> (<literal>p</>)</term> <term><symbol>DEPENDENCY_PIN</> (<literal>p</>)</term>
<listitem> <listitem>
...@@ -2848,6 +2868,101 @@ ...@@ -2848,6 +2868,101 @@
</sect1> </sect1>
<sect1 id="catalog-pg-extension">
<title><structname>pg_extension</structname></title>
<indexterm zone="catalog-pg-extension">
<primary>pg_extension</primary>
</indexterm>
<para>
The catalog <structname>pg_extension</structname> stores information
about the installed extensions. See <xref linkend="extend-extensions">
for details about extensions.
</para>
<table>
<title><structname>pg_extension</> 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>extname</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
<entry>Name of the extension</entry>
</row>
<row>
<entry><structfield>extowner</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 extension</entry>
</row>
<row>
<entry><structfield>extnamespace</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
<entry>Schema containing the extension's exported objects</entry>
</row>
<row>
<entry><structfield>extrelocatable</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>True if extension can be relocated to another schema</entry>
</row>
<row>
<entry><structfield>extversion</structfield></entry>
<entry><type>text</type></entry>
<entry></entry>
<entry>Version string for the extension, or <literal>NULL</> if none</entry>
</row>
<row>
<entry><structfield>extconfig</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
<entry>Array of <type>regclass</> OIDs for the extension's configuration
table(s), or <literal>NULL</> if none</entry>
</row>
<row>
<entry><structfield>extcondition</structfield></entry>
<entry><type>text[]</type></entry>
<entry></entry>
<entry>Array of <literal>WHERE</>-clause filter conditions for the
extension's configuration table(s), or <literal>NULL</> if none</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Note that unlike most catalogs with a <quote>namespace</> column,
<structfield>extnamespace</structfield> is not meant to imply
that the extension belongs to that schema. Extension names are never
schema-qualified. Rather, <structfield>extnamespace</structfield>
indicates the schema that contains most or all of the extension's
objects. If <structfield>extrelocatable</structfield> is true, then
this schema must in fact contain all schema-qualifiable objects
belonging to the extension.
</para>
</sect1>
<sect1 id="catalog-pg-foreign-data-wrapper"> <sect1 id="catalog-pg-foreign-data-wrapper">
<title><structname>pg_foreign_data_wrapper</structname></title> <title><structname>pg_foreign_data_wrapper</structname></title>
...@@ -6191,6 +6306,11 @@ ...@@ -6191,6 +6306,11 @@
</thead> </thead>
<tbody> <tbody>
<row>
<entry><link linkend="view-pg-available-extensions"><structname>pg_available_extensions</structname></link></entry>
<entry>available extensions</entry>
</row>
<row> <row>
<entry><link linkend="view-pg-cursors"><structname>pg_cursors</structname></link></entry> <entry><link linkend="view-pg-cursors"><structname>pg_cursors</structname></link></entry>
<entry>open cursors</entry> <entry>open cursors</entry>
...@@ -6286,6 +6406,81 @@ ...@@ -6286,6 +6406,81 @@
</table> </table>
</sect1> </sect1>
<sect1 id="view-pg-available-extensions">
<title><structname>pg_available_extensions</structname></title>
<indexterm zone="view-pg-available-extensions">
<primary>pg_available_extensions</primary>
</indexterm>
<para>
The <structname>pg_available_extensions</structname> view lists the
extensions that are available for installation. This view can only
be read by superusers. See also the
<link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>
catalog, which shows the extensions currently installed.
</para>
<table>
<title><structname>pg_available_extensions</> Columns</title>
<tgroup cols="3">
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><structfield>name</structfield></entry>
<entry><type>name</type></entry>
<entry>Extension name</entry>
</row>
<row>
<entry><structfield>version</structfield></entry>
<entry><type>text</type></entry>
<entry>Version string from the extension's control file</entry>
</row>
<row>
<entry><structfield>installed</structfield></entry>
<entry><type>text</type></entry>
<entry>Currently installed version of the extension,
or <literal>NULL</literal> if not installed</entry>
</row>
<row>
<entry><structfield>schema</structfield></entry>
<entry><type>name</type></entry>
<entry>Name of the schema where the extension is installed,
or <literal>NULL</literal> if not installed</entry>
</row>
<row>
<entry><structfield>relocatable</structfield></entry>
<entry><type>bool</type></entry>
<entry>True if extension can be relocated to another schema</entry>
</row>
<row>
<entry><structfield>comment</structfield></entry>
<entry><type>text</type></entry>
<entry>Comment string from the extension's control file</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
The <structname>pg_available_extensions</structname> view is read only.
</para>
</sect1>
<sect1 id="view-pg-cursors"> <sect1 id="view-pg-cursors">
<title><structname>pg_cursors</structname></title> <title><structname>pg_cursors</structname></title>
......
This diff is collapsed.
...@@ -11,6 +11,7 @@ Complete list of usable sgml source files in this directory. ...@@ -11,6 +11,7 @@ Complete list of usable sgml source files in this directory.
<!entity alterDatabase system "alter_database.sgml"> <!entity alterDatabase system "alter_database.sgml">
<!entity alterDefaultPrivileges system "alter_default_privileges.sgml"> <!entity alterDefaultPrivileges system "alter_default_privileges.sgml">
<!entity alterDomain system "alter_domain.sgml"> <!entity alterDomain system "alter_domain.sgml">
<!entity alterExtension system "alter_extension.sgml">
<!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml"> <!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml">
<!entity alterForeignTable system "alter_foreign_table.sgml"> <!entity alterForeignTable system "alter_foreign_table.sgml">
<!entity alterFunction system "alter_function.sgml"> <!entity alterFunction system "alter_function.sgml">
...@@ -50,6 +51,7 @@ Complete list of usable sgml source files in this directory. ...@@ -50,6 +51,7 @@ Complete list of usable sgml source files in this directory.
<!entity createConversion system "create_conversion.sgml"> <!entity createConversion system "create_conversion.sgml">
<!entity createDatabase system "create_database.sgml"> <!entity createDatabase system "create_database.sgml">
<!entity createDomain system "create_domain.sgml"> <!entity createDomain system "create_domain.sgml">
<!entity createExtension system "create_extension.sgml">
<!entity createForeignDataWrapper system "create_foreign_data_wrapper.sgml"> <!entity createForeignDataWrapper system "create_foreign_data_wrapper.sgml">
<!entity createForeignTable system "create_foreign_table.sgml"> <!entity createForeignTable system "create_foreign_table.sgml">
<!entity createFunction system "create_function.sgml"> <!entity createFunction system "create_function.sgml">
...@@ -86,6 +88,7 @@ Complete list of usable sgml source files in this directory. ...@@ -86,6 +88,7 @@ Complete list of usable sgml source files in this directory.
<!entity dropConversion system "drop_conversion.sgml"> <!entity dropConversion system "drop_conversion.sgml">
<!entity dropDatabase system "drop_database.sgml"> <!entity dropDatabase system "drop_database.sgml">
<!entity dropDomain system "drop_domain.sgml"> <!entity dropDomain system "drop_domain.sgml">
<!entity dropExtension system "drop_extension.sgml">
<!entity dropForeignDataWrapper system "drop_foreign_data_wrapper.sgml"> <!entity dropForeignDataWrapper system "drop_foreign_data_wrapper.sgml">
<!entity dropForeignTable system "drop_foreign_table.sgml"> <!entity dropForeignTable system "drop_foreign_table.sgml">
<!entity dropFunction system "drop_function.sgml"> <!entity dropFunction system "drop_function.sgml">
......
<!--
doc/src/sgml/ref/alter_extension.sgml
PostgreSQL documentation
-->
<refentry id="SQL-ALTEREXTENSION">
<refmeta>
<refentrytitle>ALTER EXTENSION</refentrytitle>
<manvolnum>7</manvolnum>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>ALTER EXTENSION</refname>
<refpurpose>
change the definition of an extension
</refpurpose>
</refnamediv>
<indexterm zone="sql-alterextension">
<primary>ALTER EXTENSION</primary>
</indexterm>
<refsynopsisdiv>
<synopsis>
ALTER EXTENSION <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>ALTER EXTENSION</command> changes the definition of an existing extension.
Currently there is only one subform:
<variablelist>
<varlistentry>
<term><literal>SET SCHEMA</literal></term>
<listitem>
<para>
This form moves the extension's objects into another schema. The
extension has to be <firstterm>relocatable</> for this command to
succeed. See <xref linkend="extend-extensions"> for details.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
<para>
<variablelist>
<varlistentry>
<term><replaceable class="PARAMETER">name</replaceable></term>
<listitem>
<para>
The name of an installed extension.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">new_schema</replaceable></term>
<listitem>
<para>
The new schema for the extension.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect1>
<refsect1>
<title>Examples</title>
<para>
To change the schema of the extension <literal>hstore</literal>
to <literal>utils</literal>:
<programlisting>
ALTER EXTENSION hstore SET SCHEMA utils;
</programlisting>
</para>
</refsect1>
<refsect1 id="SQL-ALTEREXTENSION-see-also">
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="sql-createextension"></member>
<member><xref linkend="sql-dropextension"></member>
</simplelist>
</refsect1>
</refentry>
...@@ -31,6 +31,7 @@ COMMENT ON ...@@ -31,6 +31,7 @@ COMMENT ON
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> | CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
DATABASE <replaceable class="PARAMETER">object_name</replaceable> | DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> | DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
EXTENSION <replaceable class="PARAMETER">object_name</replaceable> |
FOREIGN TABLE <replaceable class="PARAMETER">object_name</replaceable> | FOREIGN TABLE <replaceable class="PARAMETER">object_name</replaceable> |
FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) | FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) |
INDEX <replaceable class="PARAMETER">object_name</replaceable> | INDEX <replaceable class="PARAMETER">object_name</replaceable> |
......
<!--
doc/src/sgml/ref/create_extension.sgml
PostgreSQL documentation
-->
<refentry id="SQL-CREATEEXTENSION">
<refmeta>
<refentrytitle>CREATE EXTENSION</refentrytitle>
<manvolnum>7</manvolnum>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>CREATE EXTENSION</refname>
<refpurpose>install an extension</refpurpose>
</refnamediv>
<indexterm zone="sql-createextension">
<primary>CREATE EXTENSION</primary>
</indexterm>
<refsynopsisdiv>
<synopsis>
CREATE EXTENSION <replaceable class="parameter">extension_name</replaceable>
[ WITH ] [ SCHEMA [=] <replaceable class="parameter">schema</replaceable> ]
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>CREATE EXTENSION</command> loads a new extension into the current
database. There must not be an extension of the same name already loaded.
</para>
<para>
Loading an extension essentially amounts to running the extension's script
file. The script will typically create new <acronym>SQL</> objects such as
functions, data types, operators and index support methods.
<command>CREATE EXTENSION</command> additionally records the identities
of all the created objects, so that they can be dropped again if
<command>DROP EXTENSION</command> is issued.
</para>
<para>
For information about writing new extensions, see
<xref linkend="extend-extensions">.
</para>
<para>
Only superusers can execute <command>CREATE EXTENSION</command>.
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
<variablelist>
<varlistentry>
<term><replaceable class="parameter">extension_name</replaceable></term>
<listitem>
<para>
The name of the extension to be
installed. <productname>PostgreSQL</productname> will create the
extension using details from the file
<literal>SHAREDIR/contrib/</literal><replaceable class="parameter">extension</replaceable><literal>.control</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">schema</replaceable></term>
<listitem>
<para>
The name of the schema in which to install the extension's
objects, given that the extension allows its contents to be
relocated. The named schema must already exist.
If not specified, and the extension's control file does not specify a
schema either, the current default object creation schema is used.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Examples</title>
<para>
Install the <link linkend="hstore">hstore</link> extension into the
current database:
<programlisting>
CREATE EXTENSION hstore;
</programlisting>
</para>
</refsect1>
<refsect1>
<title>Compatibility</title>
<para>
<command>CREATE EXTENSION</command> is a <productname>PostgreSQL</>
extension.
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="sql-alterextension"></member>
<member><xref linkend="sql-dropextension"></member>
</simplelist>
</refsect1>
</refentry>
<!--
doc/src/sgml/ref/drop_extension.sgml
PostgreSQL documentation
-->
<refentry id="SQL-DROPEXTENSION">
<refmeta>
<refentrytitle>DROP EXTENSION</refentrytitle>
<manvolnum>7</manvolnum>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>DROP EXTENSION</refname>
<refpurpose>remove an extension</refpurpose>
</refnamediv>
<indexterm zone="sql-dropextension">
<primary>DROP EXTENSION</primary>
</indexterm>
<refsynopsisdiv>
<synopsis>
DROP EXTENSION [ IF EXISTS ] <replaceable class="PARAMETER">extension_name</replaceable> [, ...] [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>DROP EXTENSION</command> removes extensions from the database.
Dropping an extension causes its component objects to be dropped as well.
</para>
<para>
An extension can only be dropped by a superuser.
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
<variablelist>
<varlistentry>
<term><literal>IF EXISTS</literal></term>
<listitem>
<para>
Do not throw an error if the extension does not exist. A notice is issued
in this case.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER">extension_name</replaceable></term>
<listitem>
<para>
The name of an installed extension.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>CASCADE</literal></term>
<listitem>
<para>
Automatically drop objects that depend on the extension.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>RESTRICT</literal></term>
<listitem>
<para>
Refuse to drop the extension if any objects depend on it (other than
its own member objects and other extensions listed in the same
<command>DROP</> command). This is the default.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Examples</title>
<para>
To remove the extension <literal>hstore</literal> from the current
database:
<programlisting>
DROP EXTENSION hstore;
</programlisting>
This command will fail if any of <literal>hstore</literal>'s objects
are in use in the database, for example if any tables have columns
of the <type>hstore</> type. Add the <literal>CASCADE</> option to
forcibly remove those dependent objects as well.
</para>
</refsect1>
<refsect1>
<title>Compatibility</title>
<para>
<command>DROP EXTENSION</command> is a <productname>PostgreSQL</>
extension.
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="sql-createextension"></member>
<member><xref linkend="sql-alterextension"></member>
</simplelist>
</refsect1>
</refentry>
...@@ -1357,7 +1357,6 @@ testdb=&gt; ...@@ -1357,7 +1357,6 @@ testdb=&gt;
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><literal>\du[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term> <term><literal>\du[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<listitem> <listitem>
...@@ -1371,6 +1370,19 @@ testdb=&gt; ...@@ -1371,6 +1370,19 @@ testdb=&gt;
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>\dx[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<listitem>
<para>
Lists installed extensions.
If <replaceable class="parameter">pattern</replaceable>
is specified, only those extensions whose names match the pattern
are listed.
If the form <literal>\dx+</literal> is used, all the objects belonging
to each matching extension are listed.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><literal>\edit</> (or <literal>\e</>) <literal> <optional> <replaceable class="parameter">filename</> </optional> <optional> <replaceable class="parameter">line_number</> </optional> </literal></term> <term><literal>\edit</> (or <literal>\e</>) <literal> <optional> <replaceable class="parameter">filename</> </optional> <optional> <replaceable class="parameter">line_number</> </optional> </literal></term>
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
&alterDatabase; &alterDatabase;
&alterDefaultPrivileges; &alterDefaultPrivileges;
&alterDomain; &alterDomain;
&alterExtension;
&alterForeignDataWrapper; &alterForeignDataWrapper;
&alterForeignTable; &alterForeignTable;
&alterFunction; &alterFunction;
...@@ -78,6 +79,7 @@ ...@@ -78,6 +79,7 @@
&createConversion; &createConversion;
&createDatabase; &createDatabase;
&createDomain; &createDomain;
&createExtension;
&createForeignDataWrapper; &createForeignDataWrapper;
&createForeignTable; &createForeignTable;
&createFunction; &createFunction;
...@@ -114,6 +116,7 @@ ...@@ -114,6 +116,7 @@
&dropConversion; &dropConversion;
&dropDatabase; &dropDatabase;
&dropDomain; &dropDomain;
&dropExtension;
&dropForeignDataWrapper; &dropForeignDataWrapper;
&dropForeignTable; &dropForeignTable;
&dropFunction; &dropFunction;
......
...@@ -3520,9 +3520,8 @@ if TG_OP = 'INSERT' and NEW.col1 = ... then ...@@ -3520,9 +3520,8 @@ if TG_OP = 'INSERT' and NEW.col1 = ... then
<listitem> <listitem>
<para> <para>
Add data and documentation installation location control to <link Add data and documentation installation location control to
linkend="xfunc-c-pgxs"><acronym>PGXS</></link> Makefiles <acronym>PGXS</> Makefiles (Mark Cave-Ayland)
(Mark Cave-Ayland)
</para> </para>
</listitem> </listitem>
......
This diff is collapsed.
...@@ -36,7 +36,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ ...@@ -36,7 +36,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
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 \
pg_ts_parser.h pg_ts_template.h \ pg_ts_parser.h pg_ts_template.h pg_extension.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h \ pg_foreign_table.h \
pg_default_acl.h pg_seclabel.h pg_collation.h \ pg_default_acl.h pg_seclabel.h pg_collation.h \
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,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_depend.h" #include "catalog/pg_depend.h"
#include "catalog/pg_extension.h"
#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_foreign_table.h" #include "catalog/pg_foreign_table.h"
...@@ -56,6 +57,7 @@ ...@@ -56,6 +57,7 @@
#include "commands/comment.h" #include "commands/comment.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/extension.h"
#include "commands/proclang.h" #include "commands/proclang.h"
#include "commands/schemacmds.h" #include "commands/schemacmds.h"
#include "commands/seclabel.h" #include "commands/seclabel.h"
...@@ -93,6 +95,7 @@ typedef struct ...@@ -93,6 +95,7 @@ typedef struct
#define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */ #define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */
#define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */ #define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */
#define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */ #define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */
#define DEPFLAG_EXTENSION 0x0010 /* reached via extension dependency */
/* expansible list of ObjectAddresses */ /* expansible list of ObjectAddresses */
...@@ -153,8 +156,8 @@ static const Oid object_classes[MAX_OCLASS] = { ...@@ -153,8 +156,8 @@ static const Oid object_classes[MAX_OCLASS] = {
ForeignDataWrapperRelationId, /* OCLASS_FDW */ ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */ ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */ UserMappingRelationId, /* OCLASS_USER_MAPPING */
ForeignTableRelationId, /* OCLASS_FOREIGN_TABLE */ DefaultAclRelationId, /* OCLASS_DEFACL */
DefaultAclRelationId /* OCLASS_DEFACL */ ExtensionRelationId /* OCLASS_EXTENSION */
}; };
...@@ -551,10 +554,12 @@ findDependentObjects(const ObjectAddress *object, ...@@ -551,10 +554,12 @@ findDependentObjects(const ObjectAddress *object,
/* no problem */ /* no problem */
break; break;
case DEPENDENCY_INTERNAL: case DEPENDENCY_INTERNAL:
case DEPENDENCY_EXTENSION:
/* /*
* This object is part of the internal implementation of * This object is part of the internal implementation of
* another object. We have three cases: * another object, or is part of the extension that is the
* other object. We have three cases:
* *
* 1. At the outermost recursion level, disallow the DROP. (We * 1. At the outermost recursion level, disallow the DROP. (We
* just ereport here, rather than proceeding, since no other * just ereport here, rather than proceeding, since no other
...@@ -726,6 +731,9 @@ findDependentObjects(const ObjectAddress *object, ...@@ -726,6 +731,9 @@ findDependentObjects(const ObjectAddress *object,
case DEPENDENCY_INTERNAL: case DEPENDENCY_INTERNAL:
subflags = DEPFLAG_INTERNAL; subflags = DEPFLAG_INTERNAL;
break; break;
case DEPENDENCY_EXTENSION:
subflags = DEPFLAG_EXTENSION;
break;
case DEPENDENCY_PIN: case DEPENDENCY_PIN:
/* /*
...@@ -836,10 +844,12 @@ reportDependentObjects(const ObjectAddresses *targetObjects, ...@@ -836,10 +844,12 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
/* /*
* If, at any stage of the recursive search, we reached the object via * If, at any stage of the recursive search, we reached the object via
* an AUTO or INTERNAL dependency, then it's okay to delete it even in * an AUTO, INTERNAL, or EXTENSION dependency, then it's okay to
* RESTRICT mode. * delete it even in RESTRICT mode.
*/ */
if (extra->flags & (DEPFLAG_AUTO | DEPFLAG_INTERNAL)) if (extra->flags & (DEPFLAG_AUTO |
DEPFLAG_INTERNAL |
DEPFLAG_EXTENSION))
{ {
/* /*
* auto-cascades are reported at DEBUG2, not msglevel. We don't * auto-cascades are reported at DEBUG2, not msglevel. We don't
...@@ -1154,6 +1164,10 @@ doDeletion(const ObjectAddress *object) ...@@ -1154,6 +1164,10 @@ doDeletion(const ObjectAddress *object)
RemoveDefaultACLById(object->objectId); RemoveDefaultACLById(object->objectId);
break; break;
case OCLASS_EXTENSION:
RemoveExtensionById(object->objectId);
break;
default: default:
elog(ERROR, "unrecognized object class: %u", elog(ERROR, "unrecognized object class: %u",
object->classId); object->classId);
...@@ -2074,12 +2088,11 @@ getObjectClass(const ObjectAddress *object) ...@@ -2074,12 +2088,11 @@ getObjectClass(const ObjectAddress *object)
case UserMappingRelationId: case UserMappingRelationId:
return OCLASS_USER_MAPPING; return OCLASS_USER_MAPPING;
case ForeignTableRelationId:
Assert(object->objectSubId == 0);
return OCLASS_FOREIGN_TABLE;
case DefaultAclRelationId: case DefaultAclRelationId:
return OCLASS_DEFACL; return OCLASS_DEFACL;
case ExtensionRelationId:
return OCLASS_EXTENSION;
} }
/* shouldn't get here */ /* shouldn't get here */
...@@ -2687,6 +2700,18 @@ getObjectDescription(const ObjectAddress *object) ...@@ -2687,6 +2700,18 @@ getObjectDescription(const ObjectAddress *object)
break; break;
} }
case OCLASS_EXTENSION:
{
char *extname;
extname = get_extension_name(object->objectId);
if (!extname)
elog(ERROR, "cache lookup failed for extension %u",
object->objectId);
appendStringInfo(&buffer, _("extension %s"), extname);
break;
}
default: default:
appendStringInfo(&buffer, "unrecognized object %u %u %d", appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId, object->classId,
......
...@@ -1160,7 +1160,8 @@ heap_create_with_catalog(const char *relname, ...@@ -1160,7 +1160,8 @@ heap_create_with_catalog(const char *relname,
* entry, so we needn't record them here. Likewise, TOAST tables don't * entry, so we needn't record them here. Likewise, TOAST tables don't
* need a namespace dependency (they live in a pinned namespace) nor an * need a namespace dependency (they live in a pinned namespace) nor an
* owner dependency (they depend indirectly through the parent table), nor * owner dependency (they depend indirectly through the parent table), nor
* should they have any ACL entries. * should they have any ACL entries. The same applies for extension
* dependencies.
* *
* Also, skip this in bootstrap mode, since we don't make dependencies * Also, skip this in bootstrap mode, since we don't make dependencies
* while bootstrapping. * while bootstrapping.
...@@ -1182,6 +1183,8 @@ heap_create_with_catalog(const char *relname, ...@@ -1182,6 +1183,8 @@ heap_create_with_catalog(const char *relname,
recordDependencyOnOwner(RelationRelationId, relid, ownerid); recordDependencyOnOwner(RelationRelationId, relid, ownerid);
recordDependencyOnCurrentExtension(&myself);
if (reloftypeid) if (reloftypeid)
{ {
referenced.classId = TypeRelationId; referenced.classId = TypeRelationId;
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h" #include "catalog/pg_conversion.h"
#include "catalog/pg_database.h" #include "catalog/pg_database.h"
#include "catalog/pg_extension.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_largeobject_metadata.h"
...@@ -46,6 +47,7 @@ ...@@ -46,6 +47,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/extension.h"
#include "commands/proclang.h" #include "commands/proclang.h"
#include "commands/tablespace.h" #include "commands/tablespace.h"
#include "commands/trigger.h" #include "commands/trigger.h"
...@@ -129,6 +131,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, ...@@ -129,6 +131,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
address = get_object_address_relobject(objtype, objname, &relation); address = get_object_address_relobject(objtype, objname, &relation);
break; break;
case OBJECT_DATABASE: case OBJECT_DATABASE:
case OBJECT_EXTENSION:
case OBJECT_TABLESPACE: case OBJECT_TABLESPACE:
case OBJECT_ROLE: case OBJECT_ROLE:
case OBJECT_SCHEMA: case OBJECT_SCHEMA:
...@@ -267,6 +270,9 @@ get_object_address_unqualified(ObjectType objtype, List *qualname) ...@@ -267,6 +270,9 @@ get_object_address_unqualified(ObjectType objtype, List *qualname)
case OBJECT_DATABASE: case OBJECT_DATABASE:
msg = gettext_noop("database name cannot be qualified"); msg = gettext_noop("database name cannot be qualified");
break; break;
case OBJECT_EXTENSION:
msg = gettext_noop("extension name cannot be qualified");
break;
case OBJECT_TABLESPACE: case OBJECT_TABLESPACE:
msg = gettext_noop("tablespace name cannot be qualified"); msg = gettext_noop("tablespace name cannot be qualified");
break; break;
...@@ -299,6 +305,11 @@ get_object_address_unqualified(ObjectType objtype, List *qualname) ...@@ -299,6 +305,11 @@ get_object_address_unqualified(ObjectType objtype, List *qualname)
address.objectId = get_database_oid(name, false); address.objectId = get_database_oid(name, false);
address.objectSubId = 0; address.objectSubId = 0;
break; break;
case OBJECT_EXTENSION:
address.classId = ExtensionRelationId;
address.objectId = get_extension_oid(name, false);
address.objectSubId = 0;
break;
case OBJECT_TABLESPACE: case OBJECT_TABLESPACE:
address.classId = TableSpaceRelationId; address.classId = TableSpaceRelationId;
address.objectId = get_tablespace_oid(name, false); address.objectId = get_tablespace_oid(name, false);
...@@ -643,6 +654,9 @@ object_exists(ObjectAddress address) ...@@ -643,6 +654,9 @@ object_exists(ObjectAddress address)
case TSConfigRelationId: case TSConfigRelationId:
cache = TSCONFIGOID; cache = TSCONFIGOID;
break; break;
case ExtensionRelationId:
indexoid = ExtensionOidIndexId;
break;
default: default:
elog(ERROR, "unrecognized classid: %u", address.classId); elog(ERROR, "unrecognized classid: %u", address.classId);
} }
......
...@@ -132,6 +132,9 @@ ConversionCreate(const char *conname, Oid connamespace, ...@@ -132,6 +132,9 @@ ConversionCreate(const char *conname, Oid connamespace,
recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup), recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
conowner); conowner);
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* Post creation hook for new conversion */ /* Post creation hook for new conversion */
InvokeObjectAccessHook(OAT_POST_CREATE, InvokeObjectAccessHook(OAT_POST_CREATE,
ConversionRelationId, HeapTupleGetOid(tup), 0); ConversionRelationId, HeapTupleGetOid(tup), 0);
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h" #include "catalog/pg_depend.h"
#include "catalog/pg_extension.h"
#include "commands/extension.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/fmgroids.h" #include "utils/fmgroids.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
...@@ -122,6 +124,28 @@ recordMultipleDependencies(const ObjectAddress *depender, ...@@ -122,6 +124,28 @@ recordMultipleDependencies(const ObjectAddress *depender,
heap_close(dependDesc, RowExclusiveLock); heap_close(dependDesc, RowExclusiveLock);
} }
/*
* If we are executing a CREATE EXTENSION operation, mark the given object
* as being a member of the extension. Otherwise, do nothing.
*
* This must be called during creation of any user-definable object type
* that could be a member of an extension.
*/
void
recordDependencyOnCurrentExtension(const ObjectAddress *object)
{
if (creating_extension)
{
ObjectAddress extension;
extension.classId = ExtensionRelationId;
extension.objectId = CurrentExtensionObject;
extension.objectSubId = 0;
recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
}
}
/* /*
* deleteDependencyRecordsFor -- delete all records with given depender * deleteDependencyRecordsFor -- delete all records with given depender
* classId/objectId. Returns the number of records deleted. * classId/objectId. Returns the number of records deleted.
...@@ -129,9 +153,14 @@ recordMultipleDependencies(const ObjectAddress *depender, ...@@ -129,9 +153,14 @@ recordMultipleDependencies(const ObjectAddress *depender,
* This is used when redefining an existing object. Links leading to the * This is used when redefining an existing object. Links leading to the
* object do not change, and links leading from it will be recreated * object do not change, and links leading from it will be recreated
* (possibly with some differences from before). * (possibly with some differences from before).
*
* If skipExtensionDeps is true, we do not delete any dependencies that
* show that the given object is a member of an extension. This avoids
* needing a lot of extra logic to fetch and recreate that dependency.
*/ */
long long
deleteDependencyRecordsFor(Oid classId, Oid objectId) deleteDependencyRecordsFor(Oid classId, Oid objectId,
bool skipExtensionDeps)
{ {
long count = 0; long count = 0;
Relation depRel; Relation depRel;
...@@ -155,6 +184,10 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId) ...@@ -155,6 +184,10 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId)
while (HeapTupleIsValid(tup = systable_getnext(scan))) while (HeapTupleIsValid(tup = systable_getnext(scan)))
{ {
if (skipExtensionDeps &&
((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
continue;
simple_heap_delete(depRel, &tup->t_self); simple_heap_delete(depRel, &tup->t_self);
count++; count++;
} }
...@@ -320,6 +353,59 @@ isObjectPinned(const ObjectAddress *object, Relation rel) ...@@ -320,6 +353,59 @@ isObjectPinned(const ObjectAddress *object, Relation rel)
*/ */
/*
* Find the extension containing the specified object, if any
*
* Returns the OID of the extension, or InvalidOid if the object does not
* belong to any extension.
*
* Extension membership is marked by an EXTENSION dependency from the object
* to the extension. Note that the result will be indeterminate if pg_depend
* contains links from this object to more than one extension ... but that
* should never happen.
*/
Oid
getExtensionOfObject(Oid classId, Oid objectId)
{
Oid result = InvalidOid;
Relation depRel;
ScanKeyData key[2];
SysScanDesc scan;
HeapTuple tup;
depRel = heap_open(DependRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_depend_classid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(classId));
ScanKeyInit(&key[1],
Anum_pg_depend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objectId));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
SnapshotNow, 2, key);
while (HeapTupleIsValid((tup = systable_getnext(scan))))
{
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
if (depform->refclassid == ExtensionRelationId &&
depform->deptype == DEPENDENCY_EXTENSION)
{
result = depform->refobjid;
break; /* no need to keep scanning */
}
}
systable_endscan(scan);
heap_close(depRel, AccessShareLock);
return result;
}
/* /*
* Detect whether a sequence is marked as "owned" by a column * Detect whether a sequence is marked as "owned" by a column
* *
......
...@@ -38,6 +38,7 @@ NamespaceCreate(const char *nspName, Oid ownerId) ...@@ -38,6 +38,7 @@ NamespaceCreate(const char *nspName, Oid ownerId)
Datum values[Natts_pg_namespace]; Datum values[Natts_pg_namespace];
NameData nname; NameData nname;
TupleDesc tupDesc; TupleDesc tupDesc;
ObjectAddress myself;
int i; int i;
/* sanity checks */ /* sanity checks */
...@@ -73,9 +74,17 @@ NamespaceCreate(const char *nspName, Oid ownerId) ...@@ -73,9 +74,17 @@ NamespaceCreate(const char *nspName, Oid ownerId)
heap_close(nspdesc, RowExclusiveLock); heap_close(nspdesc, RowExclusiveLock);
/* Record dependency on owner */ /* Record dependencies */
myself.classId = NamespaceRelationId;
myself.objectId = nspoid;
myself.objectSubId = 0;
/* dependency on owner */
recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* Post creation hook for new schema */ /* Post creation hook for new schema */
InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0); InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0);
......
...@@ -777,7 +777,7 @@ makeOperatorDependencies(HeapTuple tuple) ...@@ -777,7 +777,7 @@ makeOperatorDependencies(HeapTuple tuple)
myself.objectSubId = 0; myself.objectSubId = 0;
/* In case we are updating a shell, delete any existing entries */ /* In case we are updating a shell, delete any existing entries */
deleteDependencyRecordsFor(myself.classId, myself.objectId); deleteDependencyRecordsFor(myself.classId, myself.objectId, false);
deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
/* Dependency on namespace */ /* Dependency on namespace */
...@@ -855,4 +855,7 @@ makeOperatorDependencies(HeapTuple tuple) ...@@ -855,4 +855,7 @@ makeOperatorDependencies(HeapTuple tuple)
/* Dependency on owner */ /* Dependency on owner */
recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple), recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
oper->oprowner); oper->oprowner);
/* Dependency on extension */
recordDependencyOnCurrentExtension(&myself);
} }
...@@ -562,10 +562,11 @@ ProcedureCreate(const char *procedureName, ...@@ -562,10 +562,11 @@ ProcedureCreate(const char *procedureName,
* Create dependencies for the new function. If we are updating an * Create dependencies for the new function. If we are updating an
* existing function, first delete any existing pg_depend entries. * existing function, first delete any existing pg_depend entries.
* (However, since we are not changing ownership or permissions, the * (However, since we are not changing ownership or permissions, the
* shared dependencies do *not* need to change, and we leave them alone.) * shared dependencies do *not* need to change, and we leave them alone.
* We also don't change any pre-existing extension-membership dependency.)
*/ */
if (is_update) if (is_update)
deleteDependencyRecordsFor(ProcedureRelationId, retval); deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
myself.classId = ProcedureRelationId; myself.classId = ProcedureRelationId;
myself.objectId = retval; myself.objectId = retval;
...@@ -615,6 +616,10 @@ ProcedureCreate(const char *procedureName, ...@@ -615,6 +616,10 @@ ProcedureCreate(const char *procedureName,
nnewmembers, newmembers); nnewmembers, newmembers);
} }
/* dependency on extension */
if (!is_update)
recordDependencyOnCurrentExtension(&myself);
heap_freetuple(tup); heap_freetuple(tup);
/* Post creation hook for new function */ /* Post creation hook for new function */
......
...@@ -481,7 +481,7 @@ TypeCreate(Oid newTypeOid, ...@@ -481,7 +481,7 @@ TypeCreate(Oid newTypeOid,
* *
* If rebuild is true, we remove existing dependencies and rebuild them * If rebuild is true, we remove existing dependencies and rebuild them
* from scratch. This is needed for ALTER TYPE, and also when replacing * from scratch. This is needed for ALTER TYPE, and also when replacing
* a shell type. * a shell type. We don't remove/rebuild extension dependencies, though.
*/ */
void void
GenerateTypeDependencies(Oid typeNamespace, GenerateTypeDependencies(Oid typeNamespace,
...@@ -507,7 +507,7 @@ GenerateTypeDependencies(Oid typeNamespace, ...@@ -507,7 +507,7 @@ GenerateTypeDependencies(Oid typeNamespace,
if (rebuild) if (rebuild)
{ {
deleteDependencyRecordsFor(TypeRelationId, typeObjectId); deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true);
deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0); deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0);
} }
...@@ -521,7 +521,7 @@ GenerateTypeDependencies(Oid typeNamespace, ...@@ -521,7 +521,7 @@ GenerateTypeDependencies(Oid typeNamespace,
* For a relation rowtype (that's not a composite type), we should skip * For a relation rowtype (that's not a composite type), we should skip
* these because we'll depend on them indirectly through the pg_class * these because we'll depend on them indirectly through the pg_class
* entry. Likewise, skip for implicit arrays since we'll depend on them * entry. Likewise, skip for implicit arrays since we'll depend on them
* through the element type. * through the element type. The same goes for extension membership.
*/ */
if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) && if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) &&
!isImplicitArray) !isImplicitArray)
...@@ -532,6 +532,10 @@ GenerateTypeDependencies(Oid typeNamespace, ...@@ -532,6 +532,10 @@ GenerateTypeDependencies(Oid typeNamespace,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); recordDependencyOnOwner(TypeRelationId, typeObjectId, owner);
/* dependency on extension */
if (!rebuild)
recordDependencyOnCurrentExtension(&myself);
} }
/* Normal dependencies on the I/O functions */ /* Normal dependencies on the I/O functions */
......
...@@ -153,6 +153,13 @@ CREATE VIEW pg_locks AS ...@@ -153,6 +153,13 @@ CREATE VIEW pg_locks AS
CREATE VIEW pg_cursors AS CREATE VIEW pg_cursors AS
SELECT * FROM pg_cursor() AS C; SELECT * FROM pg_cursor() AS C;
CREATE VIEW pg_available_extensions AS
SELECT E.name, E.version, X.extversion AS installed,
N.nspname AS schema, E.relocatable, E.comment
FROM pg_available_extensions() AS E
LEFT JOIN pg_extension AS X ON E.name = X.extname
LEFT JOIN pg_namespace AS N on N.oid = X.extnamespace;
CREATE VIEW pg_prepared_xacts AS CREATE VIEW pg_prepared_xacts AS
SELECT P.transaction, P.gid, P.prepared, SELECT P.transaction, P.gid, P.prepared,
U.rolname AS owner, D.datname AS database U.rolname AS owner, D.datname AS database
......
...@@ -14,7 +14,8 @@ include $(top_builddir)/src/Makefile.global ...@@ -14,7 +14,8 @@ include $(top_builddir)/src/Makefile.global
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \ OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
constraint.o conversioncmds.o copy.o \ constraint.o conversioncmds.o copy.o \
dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \ dbcommands.o define.o discard.o explain.o extension.o \
foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \ portalcmds.o prepare.o proclang.o \
schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \ schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "commands/conversioncmds.h" #include "commands/conversioncmds.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/extension.h"
#include "commands/proclang.h" #include "commands/proclang.h"
#include "commands/schemacmds.h" #include "commands/schemacmds.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
...@@ -188,6 +189,10 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) ...@@ -188,6 +189,10 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
AlterConversionNamespace(stmt->object, stmt->newschema); AlterConversionNamespace(stmt->object, stmt->newschema);
break; break;
case OBJECT_EXTENSION:
AlterExtensionNamespace(stmt->object, stmt->newschema);
break;
case OBJECT_FUNCTION: case OBJECT_FUNCTION:
AlterFunctionNamespace(stmt->object, stmt->objarg, false, AlterFunctionNamespace(stmt->object, stmt->objarg, false,
stmt->newschema); stmt->newschema);
...@@ -241,88 +246,205 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) ...@@ -241,88 +246,205 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
} }
} }
/*
* Change an object's namespace given its classOid and object Oid.
*
* Objects that don't have a namespace should be ignored.
*
* This function is currently used only by ALTER EXTENSION SET SCHEMA,
* so it only needs to cover object types that can be members of an
* extension, and it doesn't have to deal with certain special cases
* such as not wanting to process array types --- those should never
* be direct members of an extension anyway.
*
* Returns the OID of the object's previous namespace, or InvalidOid if
* object doesn't have a schema.
*/
Oid
AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid)
{
Oid oldNspOid = InvalidOid;
ObjectAddress dep;
dep.classId = classId;
dep.objectId = objid;
dep.objectSubId = 0;
switch (getObjectClass(&dep))
{
case OCLASS_CLASS:
{
Relation rel;
Relation classRel;
rel = relation_open(objid, AccessExclusiveLock);
oldNspOid = RelationGetNamespace(rel);
classRel = heap_open(RelationRelationId, RowExclusiveLock);
AlterRelationNamespaceInternal(classRel,
objid,
oldNspOid,
nspOid,
true);
heap_close(classRel, RowExclusiveLock);
relation_close(rel, NoLock);
break;
}
case OCLASS_PROC:
oldNspOid = AlterFunctionNamespace_oid(objid, nspOid);
break;
case OCLASS_TYPE:
oldNspOid = AlterTypeNamespace_oid(objid, nspOid);
break;
case OCLASS_CONVERSION:
oldNspOid = AlterConversionNamespace_oid(objid, nspOid);
break;
case OCLASS_OPERATOR:
oldNspOid = AlterOperatorNamespace_oid(objid, nspOid);
break;
case OCLASS_OPCLASS:
oldNspOid = AlterOpClassNamespace_oid(objid, nspOid);
break;
case OCLASS_OPFAMILY:
oldNspOid = AlterOpFamilyNamespace_oid(objid, nspOid);
break;
case OCLASS_TSPARSER:
oldNspOid = AlterTSParserNamespace_oid(objid, nspOid);
break;
case OCLASS_TSDICT:
oldNspOid = AlterTSDictionaryNamespace_oid(objid, nspOid);
break;
case OCLASS_TSTEMPLATE:
oldNspOid = AlterTSTemplateNamespace_oid(objid, nspOid);
break;
case OCLASS_TSCONFIG:
oldNspOid = AlterTSConfigurationNamespace_oid(objid, nspOid);
break;
default:
break;
}
return oldNspOid;
}
/* /*
* Generic function to change the namespace of a given object, for simple * Generic function to change the namespace of a given object, for simple
* cases (won't work for tables or functions, objects which have more than 2 * cases (won't work for tables, nor other cases where we need to do more
* key-attributes to use when searching for their syscache entries --- we * than change the namespace column of a single catalog entry).
* don't want nor need to get this generic here).
* *
* The AlterFooNamespace() calls just above will call a function whose job * The AlterFooNamespace() calls just above will call a function whose job
* is to lookup the arguments for the generic function here. * is to lookup the arguments for the generic function here.
* *
* Relation must already by open, it's the responsibility of the caller to * rel: catalog relation containing object (RowExclusiveLock'd by caller)
* close it. * oidCacheId: syscache that indexes this catalog by OID
* nameCacheId: syscache that indexes this catalog by name and namespace
* (pass -1 if there is none)
* objid: OID of object to change the namespace of
* nspOid: OID of new namespace
* Anum_name: column number of catalog's name column
* Anum_namespace: column number of catalog's namespace column
* Anum_owner: column number of catalog's owner column, or -1 if none
* acl_kind: ACL type for object, or -1 if none assigned
*
* If the object does not have an owner or permissions, pass -1 for
* Anum_owner and acl_kind. In this case the calling user must be superuser.
*
* Returns the OID of the object's previous namespace.
*/ */
void Oid
AlterObjectNamespace(Relation rel, int cacheId, AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
Oid classId, Oid objid, Oid nspOid, Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner, int Anum_name, int Anum_namespace, int Anum_owner,
AclObjectKind acl_kind, AclObjectKind acl_kind)
bool superuser_only)
{ {
Oid classId = RelationGetRelid(rel);
Oid oldNspOid; Oid oldNspOid;
Datum name, namespace; Datum name, namespace;
bool isnull; bool isnull;
HeapTuple tup, newtup = NULL; HeapTuple tup, newtup;
Datum *values; Datum *values;
bool *nulls; bool *nulls;
bool *replaces; bool *replaces;
tup = SearchSysCacheCopy1(cacheId, ObjectIdGetDatum(objid)); tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
if (!HeapTupleIsValid(tup)) /* should not happen */ if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for object %u: %s", elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
objid, getObjectDescriptionOids(classId, objid)); objid, RelationGetRelationName(rel));
name = heap_getattr(tup, Anum_name, rel->rd_att, &isnull); name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
namespace = heap_getattr(tup, Anum_namespace, rel->rd_att, &isnull); Assert(!isnull);
namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel), &isnull);
Assert(!isnull);
oldNspOid = DatumGetObjectId(namespace); oldNspOid = DatumGetObjectId(namespace);
/* Check basic namespace related issues */ /* Check basic namespace related issues */
CheckSetNamespace(oldNspOid, nspOid, classId, objid); CheckSetNamespace(oldNspOid, nspOid, classId, objid);
/* check for duplicate name (more friendly than unique-index failure) */ /* Permission checks ... superusers can always do it */
if (SearchSysCacheExists2(cacheId, name, ObjectIdGetDatum(nspOid)))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("%s already exists in schema \"%s\"",
getObjectDescriptionOids(classId, objid),
get_namespace_name(nspOid))));
/* Superusers can always do it */
if (!superuser()) if (!superuser())
{ {
Datum owner; Datum owner;
Oid ownerId; Oid ownerId;
AclResult aclresult; AclResult aclresult;
if (superuser_only) /* Fail if object does not have an explicit owner */
if (Anum_owner <= 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to SET SCHEMA of %s", (errmsg("must be superuser to SET SCHEMA of %s",
getObjectDescriptionOids(classId, objid))))); getObjectDescriptionOids(classId, objid)))));
/* Otherwise, must be owner of the existing object */ /* Otherwise, must be owner of the existing object */
owner = heap_getattr(tup, Anum_owner, rel->rd_att, &isnull); owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
Assert(!isnull);
ownerId = DatumGetObjectId(owner); ownerId = DatumGetObjectId(owner);
if (!has_privs_of_role(GetUserId(), ownerId)) if (!has_privs_of_role(GetUserId(), ownerId))
aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind, aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind,
NameStr(*(DatumGetName(name)))); NameStr(*(DatumGetName(name))));
/* owner must have CREATE privilege on namespace */ /* User must have CREATE privilege on new namespace */
aclresult = pg_namespace_aclcheck(oldNspOid, GetUserId(), ACL_CREATE); aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK) if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(oldNspOid)); get_namespace_name(nspOid));
} }
/* Prepare to update tuple */ /*
values = palloc0(rel->rd_att->natts * sizeof(Datum)); * Check for duplicate name (more friendly than unique-index failure).
nulls = palloc0(rel->rd_att->natts * sizeof(bool)); * Since this is just a friendliness check, we can just skip it in cases
replaces = palloc0(rel->rd_att->natts * sizeof(bool)); * where there isn't a suitable syscache available.
values[Anum_namespace - 1] = nspOid; */
if (nameCacheId >= 0 &&
SearchSysCacheExists2(nameCacheId, name, ObjectIdGetDatum(nspOid)))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("%s already exists in schema \"%s\"",
getObjectDescriptionOids(classId, objid),
get_namespace_name(nspOid))));
/* Build modified tuple */
values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
replaces[Anum_namespace - 1] = true; replaces[Anum_namespace - 1] = true;
newtup = heap_modify_tuple(tup, rel->rd_att, values, nulls, replaces); newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
values, nulls, replaces);
/* Perform actual update */ /* Perform actual update */
simple_heap_update(rel, &tup->t_self, newtup); simple_heap_update(rel, &tup->t_self, newtup);
...@@ -336,6 +458,8 @@ AlterObjectNamespace(Relation rel, int cacheId, ...@@ -336,6 +458,8 @@ AlterObjectNamespace(Relation rel, int cacheId,
/* update dependencies to point to the new schema */ /* update dependencies to point to the new schema */
changeDependencyFor(classId, objid, changeDependencyFor(classId, objid,
NamespaceRelationId, oldNspOid, nspOid); NamespaceRelationId, oldNspOid, nspOid);
return oldNspOid;
} }
......
...@@ -1277,7 +1277,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, ...@@ -1277,7 +1277,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
if (relform1->reltoastrelid) if (relform1->reltoastrelid)
{ {
count = deleteDependencyRecordsFor(RelationRelationId, count = deleteDependencyRecordsFor(RelationRelationId,
relform1->reltoastrelid); relform1->reltoastrelid,
false);
if (count != 1) if (count != 1)
elog(ERROR, "expected one dependency record for TOAST table, found %ld", elog(ERROR, "expected one dependency record for TOAST table, found %ld",
count); count);
...@@ -1285,7 +1286,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, ...@@ -1285,7 +1286,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
if (relform2->reltoastrelid) if (relform2->reltoastrelid)
{ {
count = deleteDependencyRecordsFor(RelationRelationId, count = deleteDependencyRecordsFor(RelationRelationId,
relform2->reltoastrelid); relform2->reltoastrelid,
false);
if (count != 1) if (count != 1)
elog(ERROR, "expected one dependency record for TOAST table, found %ld", elog(ERROR, "expected one dependency record for TOAST table, found %ld",
count); count);
......
...@@ -143,6 +143,12 @@ CommentObject(CommentStmt *stmt) ...@@ -143,6 +143,12 @@ CommentObject(CommentStmt *stmt)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to comment on procedural language"))); errmsg("must be superuser to comment on procedural language")));
break; break;
case OBJECT_EXTENSION:
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to comment on extension")));
break;
case OBJECT_OPCLASS: case OBJECT_OPCLASS:
if (!pg_opclass_ownercheck(address.objectId, GetUserId())) if (!pg_opclass_ownercheck(address.objectId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
......
...@@ -345,12 +345,35 @@ AlterConversionNamespace(List *name, const char *newschema) ...@@ -345,12 +345,35 @@ AlterConversionNamespace(List *name, const char *newschema)
/* get schema OID */ /* get schema OID */
nspOid = LookupCreationNamespace(newschema); nspOid = LookupCreationNamespace(newschema);
AlterObjectNamespace(rel, CONVOID, ConversionRelationId, convOid, nspOid, AlterObjectNamespace(rel, CONVOID, CONNAMENSP,
convOid, nspOid,
Anum_pg_conversion_conname, Anum_pg_conversion_conname,
Anum_pg_conversion_connamespace, Anum_pg_conversion_connamespace,
Anum_pg_conversion_conowner, Anum_pg_conversion_conowner,
ACL_KIND_CONVERSION, ACL_KIND_CONVERSION);
false);
heap_close(rel, NoLock); heap_close(rel, RowExclusiveLock);
}
/*
* Change conversion schema, by oid
*/
Oid
AlterConversionNamespace_oid(Oid convOid, Oid newNspOid)
{
Oid oldNspOid;
Relation rel;
rel = heap_open(ConversionRelationId, RowExclusiveLock);
oldNspOid = AlterObjectNamespace(rel, CONVOID, CONNAMENSP,
convOid, newNspOid,
Anum_pg_conversion_conname,
Anum_pg_conversion_connamespace,
Anum_pg_conversion_conowner,
ACL_KIND_CONVERSION);
heap_close(rel, RowExclusiveLock);
return oldNspOid;
} }
This diff is collapsed.
...@@ -342,6 +342,8 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) ...@@ -342,6 +342,8 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
Oid fdwvalidator; Oid fdwvalidator;
Datum fdwoptions; Datum fdwoptions;
Oid ownerId; Oid ownerId;
ObjectAddress myself;
ObjectAddress referenced;
/* Must be super user */ /* Must be super user */
if (!superuser()) if (!superuser())
...@@ -401,15 +403,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) ...@@ -401,15 +403,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
heap_freetuple(tuple); heap_freetuple(tuple);
if (fdwvalidator) /* record dependencies */
{
ObjectAddress myself;
ObjectAddress referenced;
myself.classId = ForeignDataWrapperRelationId; myself.classId = ForeignDataWrapperRelationId;
myself.objectId = fdwId; myself.objectId = fdwId;
myself.objectSubId = 0; myself.objectSubId = 0;
if (fdwvalidator)
{
referenced.classId = ProcedureRelationId; referenced.classId = ProcedureRelationId;
referenced.objectId = fdwvalidator; referenced.objectId = fdwvalidator;
referenced.objectSubId = 0; referenced.objectSubId = 0;
...@@ -418,6 +418,9 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) ...@@ -418,6 +418,9 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId); recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* Post creation hook for new foreign data wrapper */ /* Post creation hook for new foreign data wrapper */
InvokeObjectAccessHook(OAT_POST_CREATE, InvokeObjectAccessHook(OAT_POST_CREATE,
ForeignDataWrapperRelationId, fdwId, 0); ForeignDataWrapperRelationId, fdwId, 0);
...@@ -691,7 +694,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) ...@@ -691,7 +694,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
heap_freetuple(tuple); heap_freetuple(tuple);
/* Add dependency on FDW and owner */ /* record dependencies */
myself.classId = ForeignServerRelationId; myself.classId = ForeignServerRelationId;
myself.objectId = srvId; myself.objectId = srvId;
myself.objectSubId = 0; myself.objectSubId = 0;
...@@ -703,6 +706,9 @@ CreateForeignServer(CreateForeignServerStmt *stmt) ...@@ -703,6 +706,9 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId); recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* Post creation hook for new foreign server */ /* Post creation hook for new foreign server */
InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0); InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
...@@ -974,8 +980,13 @@ CreateUserMapping(CreateUserMappingStmt *stmt) ...@@ -974,8 +980,13 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
if (OidIsValid(useId)) if (OidIsValid(useId))
{
/* Record the mapped user dependency */ /* Record the mapped user dependency */
recordDependencyOnOwner(UserMappingRelationId, umId, useId); recordDependencyOnOwner(UserMappingRelationId, umId, useId);
}
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* Post creation hook for new user mapping */ /* Post creation hook for new user mapping */
InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0); InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
......
...@@ -1762,6 +1762,9 @@ CreateCast(CreateCastStmt *stmt) ...@@ -1762,6 +1762,9 @@ CreateCast(CreateCastStmt *stmt)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
} }
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* Post creation hook for new cast */ /* Post creation hook for new cast */
InvokeObjectAccessHook(OAT_POST_CREATE, InvokeObjectAccessHook(OAT_POST_CREATE,
CastRelationId, myself.objectId, 0); CastRelationId, myself.objectId, 0);
...@@ -1875,13 +1878,7 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, ...@@ -1875,13 +1878,7 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
const char *newschema) const char *newschema)
{ {
Oid procOid; Oid procOid;
Oid oldNspOid;
Oid nspOid; Oid nspOid;
HeapTuple tup;
Relation procRel;
Form_pg_proc proc;
procRel = heap_open(ProcedureRelationId, RowExclusiveLock);
/* get function OID */ /* get function OID */
if (isagg) if (isagg)
...@@ -1889,20 +1886,33 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, ...@@ -1889,20 +1886,33 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
else else
procOid = LookupFuncNameTypeNames(name, argtypes, false); procOid = LookupFuncNameTypeNames(name, argtypes, false);
/* check permissions on function */ /* get schema OID and check its permissions */
if (!pg_proc_ownercheck(procOid, GetUserId())) nspOid = LookupCreationNamespace(newschema);
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(name)); AlterFunctionNamespace_oid(procOid, nspOid);
}
Oid
AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
{
Oid oldNspOid;
HeapTuple tup;
Relation procRel;
Form_pg_proc proc;
procRel = heap_open(ProcedureRelationId, RowExclusiveLock);
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid)); tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
if (!HeapTupleIsValid(tup)) if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for function %u", procOid); elog(ERROR, "cache lookup failed for function %u", procOid);
proc = (Form_pg_proc) GETSTRUCT(tup); proc = (Form_pg_proc) GETSTRUCT(tup);
oldNspOid = proc->pronamespace; /* check permissions on function */
if (!pg_proc_ownercheck(procOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameStr(proc->proname));
/* get schema OID and check its permissions */ oldNspOid = proc->pronamespace;
nspOid = LookupCreationNamespace(newschema);
/* common checks on switching namespaces */ /* common checks on switching namespaces */
CheckSetNamespace(oldNspOid, nspOid, ProcedureRelationId, procOid); CheckSetNamespace(oldNspOid, nspOid, ProcedureRelationId, procOid);
...@@ -1916,7 +1926,7 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, ...@@ -1916,7 +1926,7 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
(errcode(ERRCODE_DUPLICATE_FUNCTION), (errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function \"%s\" already exists in schema \"%s\"", errmsg("function \"%s\" already exists in schema \"%s\"",
NameStr(proc->proname), NameStr(proc->proname),
newschema))); get_namespace_name(nspOid))));
/* OK, modify the pg_proc row */ /* OK, modify the pg_proc row */
...@@ -1930,11 +1940,13 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, ...@@ -1930,11 +1940,13 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
if (changeDependencyFor(ProcedureRelationId, procOid, if (changeDependencyFor(ProcedureRelationId, procOid,
NamespaceRelationId, oldNspOid, nspOid) != 1) NamespaceRelationId, oldNspOid, nspOid) != 1)
elog(ERROR, "failed to change schema dependency for function \"%s\"", elog(ERROR, "failed to change schema dependency for function \"%s\"",
NameListToString(name)); NameStr(proc->proname));
heap_freetuple(tup); heap_freetuple(tup);
heap_close(procRel, RowExclusiveLock); heap_close(procRel, RowExclusiveLock);
return oldNspOid;
} }
......
...@@ -309,6 +309,9 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) ...@@ -309,6 +309,9 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
/* dependency on owner */ /* dependency on owner */
recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId()); recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* Post creation hook for new operator family */ /* Post creation hook for new operator family */
InvokeObjectAccessHook(OAT_POST_CREATE, InvokeObjectAccessHook(OAT_POST_CREATE,
OperatorFamilyRelationId, opfamilyoid, 0); OperatorFamilyRelationId, opfamilyoid, 0);
...@@ -709,6 +712,9 @@ DefineOpClass(CreateOpClassStmt *stmt) ...@@ -709,6 +712,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
/* dependency on owner */ /* dependency on owner */
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId()); recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* Post creation hook for new operator class */ /* Post creation hook for new operator class */
InvokeObjectAccessHook(OAT_POST_CREATE, InvokeObjectAccessHook(OAT_POST_CREATE,
OperatorClassRelationId, opclassoid, 0); OperatorClassRelationId, opclassoid, 0);
...@@ -1997,28 +2003,48 @@ AlterOpClassNamespace(List *name, char *access_method, const char *newschema) ...@@ -1997,28 +2003,48 @@ AlterOpClassNamespace(List *name, char *access_method, const char *newschema)
{ {
Oid amOid; Oid amOid;
Relation rel; Relation rel;
Oid oid; Oid opclassOid;
Oid nspOid; Oid nspOid;
amOid = get_am_oid(access_method, false); amOid = get_am_oid(access_method, false);
rel = heap_open(OperatorClassRelationId, RowExclusiveLock); rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
/* Look up the opclass. */ /* Look up the opclass */
oid = get_opclass_oid(amOid, name, false); opclassOid = get_opclass_oid(amOid, name, false);
/* get schema OID */ /* get schema OID */
nspOid = LookupCreationNamespace(newschema); nspOid = LookupCreationNamespace(newschema);
AlterObjectNamespace(rel, CLAOID, OperatorClassRelationId, AlterObjectNamespace(rel, CLAOID, -1,
oid, nspOid, opclassOid, nspOid,
Anum_pg_opfamily_opfname, Anum_pg_opclass_opcname,
Anum_pg_opfamily_opfnamespace, Anum_pg_opclass_opcnamespace,
Anum_pg_opfamily_opfowner, Anum_pg_opclass_opcowner,
ACL_KIND_OPCLASS, ACL_KIND_OPCLASS);
false);
heap_close(rel, NoLock); heap_close(rel, RowExclusiveLock);
}
Oid
AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid)
{
Oid oldNspOid;
Relation rel;
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
oldNspOid =
AlterObjectNamespace(rel, CLAOID, -1,
opclassOid, newNspOid,
Anum_pg_opclass_opcname,
Anum_pg_opclass_opcnamespace,
Anum_pg_opclass_opcowner,
ACL_KIND_OPCLASS);
heap_close(rel, RowExclusiveLock);
return oldNspOid;
} }
/* /*
...@@ -2186,26 +2212,46 @@ AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema) ...@@ -2186,26 +2212,46 @@ AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema)
{ {
Oid amOid; Oid amOid;
Relation rel; Relation rel;
Oid opfamilyOid;
Oid nspOid; Oid nspOid;
Oid oid;
amOid = get_am_oid(access_method, false); amOid = get_am_oid(access_method, false);
rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
/* Look up the opfamily */ /* Look up the opfamily */
oid = get_opfamily_oid(amOid, name, false); opfamilyOid = get_opfamily_oid(amOid, name, false);
/* get schema OID */ /* get schema OID */
nspOid = LookupCreationNamespace(newschema); nspOid = LookupCreationNamespace(newschema);
AlterObjectNamespace(rel, OPFAMILYOID, OperatorFamilyRelationId, AlterObjectNamespace(rel, OPFAMILYOID, -1,
oid, nspOid, opfamilyOid, nspOid,
Anum_pg_opfamily_opfname, Anum_pg_opfamily_opfname,
Anum_pg_opfamily_opfnamespace, Anum_pg_opfamily_opfnamespace,
Anum_pg_opfamily_opfowner, Anum_pg_opfamily_opfowner,
ACL_KIND_OPFAMILY, ACL_KIND_OPFAMILY);
false);
heap_close(rel, NoLock); heap_close(rel, RowExclusiveLock);
}
Oid
AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid)
{
Oid oldNspOid;
Relation rel;
rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
oldNspOid =
AlterObjectNamespace(rel, OPFAMILYOID, -1,
opfamilyOid, newNspOid,
Anum_pg_opfamily_opfname,
Anum_pg_opfamily_opfnamespace,
Anum_pg_opfamily_opfowner,
ACL_KIND_OPFAMILY);
heap_close(rel, RowExclusiveLock);
return oldNspOid;
} }
...@@ -477,12 +477,32 @@ AlterOperatorNamespace(List *names, List *argtypes, const char *newschema) ...@@ -477,12 +477,32 @@ AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
/* get schema OID */ /* get schema OID */
nspOid = LookupCreationNamespace(newschema); nspOid = LookupCreationNamespace(newschema);
AlterObjectNamespace(rel, OPEROID, OperatorRelationId, operOid, nspOid, AlterObjectNamespace(rel, OPEROID, -1,
operOid, nspOid,
Anum_pg_operator_oprname, Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace, Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner, Anum_pg_operator_oprowner,
ACL_KIND_OPER, ACL_KIND_OPER);
false);
heap_close(rel, NoLock); heap_close(rel, RowExclusiveLock);
}
Oid
AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid)
{
Oid oldNspOid;
Relation rel;
rel = heap_open(OperatorRelationId, RowExclusiveLock);
oldNspOid = AlterObjectNamespace(rel, OPEROID, -1,
operOid, newNspOid,
Anum_pg_operator_oprname,
Anum_pg_operator_oprnamespace,
Anum_pg_operator_oprowner,
ACL_KIND_OPER);
heap_close(rel, RowExclusiveLock);
return oldNspOid;
} }
...@@ -388,20 +388,25 @@ create_proc_lang(const char *languageName, bool replace, ...@@ -388,20 +388,25 @@ create_proc_lang(const char *languageName, bool replace,
* Create dependencies for the new language. If we are updating an * Create dependencies for the new language. If we are updating an
* existing language, first delete any existing pg_depend entries. * existing language, first delete any existing pg_depend entries.
* (However, since we are not changing ownership or permissions, the * (However, since we are not changing ownership or permissions, the
* shared dependencies do *not* need to change, and we leave them alone.) * shared dependencies do *not* need to change, and we leave them alone.
* We also don't change any pre-existing extension-membership dependency.)
*/ */
myself.classId = LanguageRelationId; myself.classId = LanguageRelationId;
myself.objectId = HeapTupleGetOid(tup); myself.objectId = HeapTupleGetOid(tup);
myself.objectSubId = 0; myself.objectSubId = 0;
if (is_update) if (is_update)
deleteDependencyRecordsFor(myself.classId, myself.objectId); deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
/* dependency on owner of language */ /* dependency on owner of language */
if (!is_update) if (!is_update)
recordDependencyOnOwner(myself.classId, myself.objectId, recordDependencyOnOwner(myself.classId, myself.objectId,
languageOwner); languageOwner);
/* dependency on extension */
if (!is_update)
recordDependencyOnCurrentExtension(&myself);
/* dependency on the PL handler function */ /* dependency on the PL handler function */
referenced.classId = ProcedureRelationId; referenced.classId = ProcedureRelationId;
referenced.objectId = handlerOid; referenced.objectId = handlerOid;
......
...@@ -6851,6 +6851,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, ...@@ -6851,6 +6851,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_FOREIGN_SERVER: case OCLASS_FOREIGN_SERVER:
case OCLASS_USER_MAPPING: case OCLASS_USER_MAPPING:
case OCLASS_DEFACL: case OCLASS_DEFACL:
case OCLASS_EXTENSION:
/* /*
* We don't expect any of these sorts of objects to depend on * We don't expect any of these sorts of objects to depend on
......
...@@ -135,6 +135,9 @@ makeParserDependencies(HeapTuple tuple) ...@@ -135,6 +135,9 @@ makeParserDependencies(HeapTuple tuple)
referenced.objectSubId = 0; referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* dependencies on functions */ /* dependencies on functions */
referenced.classId = ProcedureRelationId; referenced.classId = ProcedureRelationId;
referenced.objectSubId = 0; referenced.objectSubId = 0;
...@@ -414,12 +417,33 @@ AlterTSParserNamespace(List *name, const char *newschema) ...@@ -414,12 +417,33 @@ AlterTSParserNamespace(List *name, const char *newschema)
/* get schema OID */ /* get schema OID */
nspOid = LookupCreationNamespace(newschema); nspOid = LookupCreationNamespace(newschema);
AlterObjectNamespace(rel, TSPARSEROID, TSParserRelationId, prsId, nspOid, AlterObjectNamespace(rel, TSPARSEROID, TSPARSERNAMENSP,
prsId, nspOid,
Anum_pg_ts_parser_prsname, Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace, Anum_pg_ts_parser_prsnamespace,
-1, -1, true); -1, -1);
heap_close(rel, NoLock); heap_close(rel, RowExclusiveLock);
}
Oid
AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid)
{
Oid oldNspOid;
Relation rel;
rel = heap_open(TSParserRelationId, RowExclusiveLock);
oldNspOid =
AlterObjectNamespace(rel, TSPARSEROID, TSPARSERNAMENSP,
prsId, newNspOid,
Anum_pg_ts_parser_prsname,
Anum_pg_ts_parser_prsnamespace,
-1, -1);
heap_close(rel, RowExclusiveLock);
return oldNspOid;
} }
/* ---------------------- TS Dictionary commands -----------------------*/ /* ---------------------- TS Dictionary commands -----------------------*/
...@@ -447,6 +471,9 @@ makeDictionaryDependencies(HeapTuple tuple) ...@@ -447,6 +471,9 @@ makeDictionaryDependencies(HeapTuple tuple)
/* dependency on owner */ /* dependency on owner */
recordDependencyOnOwner(myself.classId, myself.objectId, dict->dictowner); recordDependencyOnOwner(myself.classId, myself.objectId, dict->dictowner);
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* dependency on template */ /* dependency on template */
referenced.classId = TSTemplateRelationId; referenced.classId = TSTemplateRelationId;
referenced.objectId = dict->dicttemplate; referenced.objectId = dict->dicttemplate;
...@@ -668,14 +695,35 @@ AlterTSDictionaryNamespace(List *name, const char *newschema) ...@@ -668,14 +695,35 @@ AlterTSDictionaryNamespace(List *name, const char *newschema)
/* get schema OID */ /* get schema OID */
nspOid = LookupCreationNamespace(newschema); nspOid = LookupCreationNamespace(newschema);
AlterObjectNamespace(rel, TSDICTOID, TSDictionaryRelationId, dictId, nspOid, AlterObjectNamespace(rel, TSDICTOID, TSDICTNAMENSP,
dictId, nspOid,
Anum_pg_ts_dict_dictname, Anum_pg_ts_dict_dictname,
Anum_pg_ts_dict_dictnamespace, Anum_pg_ts_dict_dictnamespace,
Anum_pg_ts_dict_dictowner, Anum_pg_ts_dict_dictowner,
ACL_KIND_TSDICTIONARY, ACL_KIND_TSDICTIONARY);
true);
heap_close(rel, NoLock); heap_close(rel, RowExclusiveLock);
}
Oid
AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
{
Oid oldNspOid;
Relation rel;
rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
oldNspOid =
AlterObjectNamespace(rel, TSDICTOID, TSDICTNAMENSP,
dictId, newNspOid,
Anum_pg_ts_dict_dictname,
Anum_pg_ts_dict_dictnamespace,
Anum_pg_ts_dict_dictowner,
ACL_KIND_TSDICTIONARY);
heap_close(rel, RowExclusiveLock);
return oldNspOid;
} }
/* /*
...@@ -1012,6 +1060,9 @@ makeTSTemplateDependencies(HeapTuple tuple) ...@@ -1012,6 +1060,9 @@ makeTSTemplateDependencies(HeapTuple tuple)
referenced.objectSubId = 0; referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself);
/* dependencies on functions */ /* dependencies on functions */
referenced.classId = ProcedureRelationId; referenced.classId = ProcedureRelationId;
referenced.objectSubId = 0; referenced.objectSubId = 0;
...@@ -1177,13 +1228,33 @@ AlterTSTemplateNamespace(List *name, const char *newschema) ...@@ -1177,13 +1228,33 @@ AlterTSTemplateNamespace(List *name, const char *newschema)
/* get schema OID */ /* get schema OID */
nspOid = LookupCreationNamespace(newschema); nspOid = LookupCreationNamespace(newschema);
AlterObjectNamespace(rel, TSTEMPLATEOID, TSTemplateRelationId, AlterObjectNamespace(rel, TSTEMPLATEOID, TSTEMPLATENAMENSP,
tmplId, nspOid, tmplId, nspOid,
Anum_pg_ts_template_tmplname, Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace, Anum_pg_ts_template_tmplnamespace,
-1, -1, true); -1, -1);
heap_close(rel, NoLock); heap_close(rel, RowExclusiveLock);
}
Oid
AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
{
Oid oldNspOid;
Relation rel;
rel = heap_open(TSTemplateRelationId, RowExclusiveLock);
oldNspOid =
AlterObjectNamespace(rel, TSTEMPLATEOID, TSTEMPLATENAMENSP,
tmplId, newNspOid,
Anum_pg_ts_template_tmplname,
Anum_pg_ts_template_tmplnamespace,
-1, -1);
heap_close(rel, RowExclusiveLock);
return oldNspOid;
} }
/* /*
...@@ -1313,10 +1384,10 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld, ...@@ -1313,10 +1384,10 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
myself.objectId = HeapTupleGetOid(tuple); myself.objectId = HeapTupleGetOid(tuple);
myself.objectSubId = 0; myself.objectSubId = 0;
/* for ALTER case, first flush old dependencies */ /* for ALTER case, first flush old dependencies, except extension deps */
if (removeOld) if (removeOld)
{ {
deleteDependencyRecordsFor(myself.classId, myself.objectId); deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
} }
...@@ -1336,6 +1407,10 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld, ...@@ -1336,6 +1407,10 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
/* dependency on owner */ /* dependency on owner */
recordDependencyOnOwner(myself.classId, myself.objectId, cfg->cfgowner); recordDependencyOnOwner(myself.classId, myself.objectId, cfg->cfgowner);
/* dependency on extension */
if (!removeOld)
recordDependencyOnCurrentExtension(&myself);
/* dependency on parser */ /* dependency on parser */
referenced.classId = TSParserRelationId; referenced.classId = TSParserRelationId;
referenced.objectId = cfg->cfgparser; referenced.objectId = cfg->cfgparser;
...@@ -1603,14 +1678,35 @@ AlterTSConfigurationNamespace(List *name, const char *newschema) ...@@ -1603,14 +1678,35 @@ AlterTSConfigurationNamespace(List *name, const char *newschema)
/* get schema OID */ /* get schema OID */
nspOid = LookupCreationNamespace(newschema); nspOid = LookupCreationNamespace(newschema);
AlterObjectNamespace(rel, TSCONFIGOID, TSConfigRelationId, cfgId, nspOid, AlterObjectNamespace(rel, TSCONFIGOID, TSCONFIGNAMENSP,
cfgId, nspOid,
Anum_pg_ts_config_cfgname, Anum_pg_ts_config_cfgname,
Anum_pg_ts_config_cfgnamespace, Anum_pg_ts_config_cfgnamespace,
Anum_pg_ts_config_cfgowner, Anum_pg_ts_config_cfgowner,
ACL_KIND_TSCONFIGURATION, ACL_KIND_TSCONFIGURATION);
false);
heap_close(rel, NoLock); heap_close(rel, RowExclusiveLock);
}
Oid
AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
{
Oid oldNspOid;
Relation rel;
rel = heap_open(TSConfigRelationId, RowExclusiveLock);
oldNspOid =
AlterObjectNamespace(rel, TSCONFIGOID, TSCONFIGNAMENSP,
cfgId, newNspOid,
Anum_pg_ts_config_cfgname,
Anum_pg_ts_config_cfgnamespace,
Anum_pg_ts_config_cfgowner,
ACL_KIND_TSCONFIGURATION);
heap_close(rel, RowExclusiveLock);
return oldNspOid;
} }
/* /*
......
...@@ -2780,20 +2780,27 @@ AlterTypeNamespace(List *names, const char *newschema) ...@@ -2780,20 +2780,27 @@ AlterTypeNamespace(List *names, const char *newschema)
TypeName *typename; TypeName *typename;
Oid typeOid; Oid typeOid;
Oid nspOid; Oid nspOid;
Oid elemOid;
/* Make a TypeName so we can use standard type lookup machinery */ /* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names); typename = makeTypeNameFromNameList(names);
typeOid = typenameTypeId(NULL, typename); typeOid = typenameTypeId(NULL, typename);
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
AlterTypeNamespace_oid(typeOid, nspOid);
}
Oid
AlterTypeNamespace_oid(Oid typeOid, Oid nspOid)
{
Oid elemOid;
/* check permissions on type */ /* check permissions on type */
if (!pg_type_ownercheck(typeOid, GetUserId())) if (!pg_type_ownercheck(typeOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
format_type_be(typeOid)); format_type_be(typeOid));
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
/* don't allow direct alteration of array types */ /* don't allow direct alteration of array types */
elemOid = get_element_type(typeOid); elemOid = get_element_type(typeOid);
if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid) if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
...@@ -2805,7 +2812,7 @@ AlterTypeNamespace(List *names, const char *newschema) ...@@ -2805,7 +2812,7 @@ AlterTypeNamespace(List *names, const char *newschema)
format_type_be(elemOid)))); format_type_be(elemOid))));
/* and do the work */ /* and do the work */
AlterTypeNamespaceInternal(typeOid, nspOid, false, true); return AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
} }
/* /*
...@@ -2820,8 +2827,10 @@ AlterTypeNamespace(List *names, const char *newschema) ...@@ -2820,8 +2827,10 @@ AlterTypeNamespace(List *names, const char *newschema)
* If errorOnTableType is TRUE, the function errors out if the type is * If errorOnTableType is TRUE, the function errors out if the type is
* a table type. ALTER TABLE has to be used to move a table to a new * a table type. ALTER TABLE has to be used to move a table to a new
* namespace. * namespace.
*
* Returns the type's old namespace OID.
*/ */
void Oid
AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray, bool isImplicitArray,
bool errorOnTableType) bool errorOnTableType)
...@@ -2928,4 +2937,6 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, ...@@ -2928,4 +2937,6 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
/* Recursively alter the associated array type, if any */ /* Recursively alter the associated array type, if any */
if (OidIsValid(arrayOid)) if (OidIsValid(arrayOid))
AlterTypeNamespaceInternal(arrayOid, nspOid, true, true); AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
return oldNspOid;
} }
...@@ -3239,6 +3239,17 @@ _copyAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *from) ...@@ -3239,6 +3239,17 @@ _copyAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *from)
return newnode; return newnode;
} }
static CreateExtensionStmt *
_copyCreateExtensionStmt(CreateExtensionStmt *from)
{
CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt);
COPY_STRING_FIELD(extname);
COPY_NODE_FIELD(options);
return newnode;
}
static CreateFdwStmt * static CreateFdwStmt *
_copyCreateFdwStmt(CreateFdwStmt *from) _copyCreateFdwStmt(CreateFdwStmt *from)
{ {
...@@ -4238,6 +4249,9 @@ copyObject(void *from) ...@@ -4238,6 +4249,9 @@ copyObject(void *from)
case T_AlterTableSpaceOptionsStmt: case T_AlterTableSpaceOptionsStmt:
retval = _copyAlterTableSpaceOptionsStmt(from); retval = _copyAlterTableSpaceOptionsStmt(from);
break; break;
case T_CreateExtensionStmt:
retval = _copyCreateExtensionStmt(from);
break;
case T_CreateFdwStmt: case T_CreateFdwStmt:
retval = _copyCreateFdwStmt(from); retval = _copyCreateFdwStmt(from);
break; break;
......
...@@ -1645,6 +1645,15 @@ _equalAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *a, ...@@ -1645,6 +1645,15 @@ _equalAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *a,
return true; return true;
} }
static bool
_equalCreateExtensionStmt(CreateExtensionStmt *a, CreateExtensionStmt *b)
{
COMPARE_STRING_FIELD(extname);
COMPARE_NODE_FIELD(options);
return true;
}
static bool static bool
_equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b) _equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b)
{ {
...@@ -2845,6 +2854,9 @@ equal(void *a, void *b) ...@@ -2845,6 +2854,9 @@ equal(void *a, void *b)
case T_AlterTableSpaceOptionsStmt: case T_AlterTableSpaceOptionsStmt:
retval = _equalAlterTableSpaceOptionsStmt(a, b); retval = _equalAlterTableSpaceOptionsStmt(a, b);
break; break;
case T_CreateExtensionStmt:
retval = _equalCreateExtensionStmt(a, b);
break;
case T_CreateFdwStmt: case T_CreateFdwStmt:
retval = _equalCreateFdwStmt(a, b); retval = _equalCreateFdwStmt(a, b);
break; break;
......
...@@ -191,7 +191,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ ...@@ -191,7 +191,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
AlterDefaultPrivilegesStmt DefACLAction AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
...@@ -227,9 +227,9 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ ...@@ -227,9 +227,9 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
%type <dbehavior> opt_drop_behavior %type <dbehavior> opt_drop_behavior
%type <list> createdb_opt_list alterdb_opt_list copy_opt_list %type <list> createdb_opt_list alterdb_opt_list copy_opt_list
transaction_mode_list transaction_mode_list create_extension_opt_list
%type <defelt> createdb_opt_item alterdb_opt_item copy_opt_item %type <defelt> createdb_opt_item alterdb_opt_item copy_opt_item
transaction_mode_item transaction_mode_item create_extension_opt_item
%type <ival> opt_lock lock_type cast_context %type <ival> opt_lock lock_type cast_context
%type <ival> vacuum_option_list vacuum_option_elem %type <ival> vacuum_option_list vacuum_option_elem
...@@ -492,7 +492,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ ...@@ -492,7 +492,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
FREEZE FROM FULL FUNCTION FUNCTIONS FREEZE FROM FULL FUNCTION FUNCTIONS
...@@ -692,6 +693,7 @@ stmt : ...@@ -692,6 +693,7 @@ stmt :
| CreateCastStmt | CreateCastStmt
| CreateConversionStmt | CreateConversionStmt
| CreateDomainStmt | CreateDomainStmt
| CreateExtensionStmt
| CreateFdwStmt | CreateFdwStmt
| CreateForeignServerStmt | CreateForeignServerStmt
| CreateForeignTableStmt | CreateForeignTableStmt
...@@ -3215,6 +3217,37 @@ DropTableSpaceStmt: DROP TABLESPACE name ...@@ -3215,6 +3217,37 @@ DropTableSpaceStmt: DROP TABLESPACE name
} }
; ;
/*****************************************************************************
*
* QUERY:
* CREATE EXTENSION extension
* [ WITH ] [ SCHEMA [=] schema ]
*
*****************************************************************************/
CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list
{
CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
n->extname = $3;
n->options = $5;
$$ = (Node *) n;
}
;
create_extension_opt_list:
create_extension_opt_list create_extension_opt_item
{ $$ = lappend($1, $2); }
| /* EMPTY */
{ $$ = NIL; }
;
create_extension_opt_item:
SCHEMA opt_equal name
{
$$ = makeDefElem("schema", (Node *)makeString($3));
}
;
/***************************************************************************** /*****************************************************************************
* *
* QUERY: * QUERY:
...@@ -4340,11 +4373,12 @@ drop_type: TABLE { $$ = OBJECT_TABLE; } ...@@ -4340,11 +4373,12 @@ drop_type: TABLE { $$ = OBJECT_TABLE; }
| SEQUENCE { $$ = OBJECT_SEQUENCE; } | SEQUENCE { $$ = OBJECT_SEQUENCE; }
| VIEW { $$ = OBJECT_VIEW; } | VIEW { $$ = OBJECT_VIEW; }
| INDEX { $$ = OBJECT_INDEX; } | INDEX { $$ = OBJECT_INDEX; }
| TYPE_P { $$ = OBJECT_TYPE; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
| TYPE_P { $$ = OBJECT_TYPE; }
| DOMAIN_P { $$ = OBJECT_DOMAIN; } | DOMAIN_P { $$ = OBJECT_DOMAIN; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; } | CONVERSION_P { $$ = OBJECT_CONVERSION; }
| SCHEMA { $$ = OBJECT_SCHEMA; } | SCHEMA { $$ = OBJECT_SCHEMA; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; } | TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; } | TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; } | TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
...@@ -4398,7 +4432,7 @@ opt_restart_seqs: ...@@ -4398,7 +4432,7 @@ opt_restart_seqs:
* *
* COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW | * COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT | * CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
* CAST | COLUMN | SCHEMA | TABLESPACE | ROLE | * CAST | COLUMN | SCHEMA | TABLESPACE | EXTENSION | ROLE |
* TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY | * TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
* TEXT SEARCH TEMPLATE | TEXT SEARCH CONFIGURATION | * TEXT SEARCH TEMPLATE | TEXT SEARCH CONFIGURATION |
* FOREIGN TABLE ] <objname> | * FOREIGN TABLE ] <objname> |
...@@ -4577,6 +4611,7 @@ comment_type: ...@@ -4577,6 +4611,7 @@ comment_type:
| VIEW { $$ = OBJECT_VIEW; } | VIEW { $$ = OBJECT_VIEW; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; } | CONVERSION_P { $$ = OBJECT_CONVERSION; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; } | TABLESPACE { $$ = OBJECT_TABLESPACE; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
| ROLE { $$ = OBJECT_ROLE; } | ROLE { $$ = OBJECT_ROLE; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
; ;
...@@ -6271,6 +6306,14 @@ AlterObjectSchemaStmt: ...@@ -6271,6 +6306,14 @@ AlterObjectSchemaStmt:
n->newschema = $6; n->newschema = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
| ALTER EXTENSION any_name SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_EXTENSION;
n->object = $3;
n->newschema = $6;
$$ = (Node *)n;
}
| ALTER FUNCTION function_with_argtypes SET SCHEMA name | ALTER FUNCTION function_with_argtypes SET SCHEMA name
{ {
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
...@@ -11462,6 +11505,7 @@ unreserved_keyword: ...@@ -11462,6 +11505,7 @@ unreserved_keyword:
| EXCLUSIVE | EXCLUSIVE
| EXECUTE | EXECUTE
| EXPLAIN | EXPLAIN
| EXTENSION
| EXTERNAL | EXTERNAL
| FAMILY | FAMILY
| FIRST_P | FIRST_P
......
...@@ -143,7 +143,7 @@ InsertRule(char *rulname, ...@@ -143,7 +143,7 @@ InsertRule(char *rulname,
/* If replacing, get rid of old dependencies and make new ones */ /* If replacing, get rid of old dependencies and make new ones */
if (is_update) if (is_update)
deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId); deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId, false);
/* /*
* Install dependency on rule's relation to ensure it will go away on * Install dependency on rule's relation to ensure it will go away on
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/discard.h" #include "commands/discard.h"
#include "commands/explain.h" #include "commands/explain.h"
#include "commands/extension.h"
#include "commands/lockcmds.h" #include "commands/lockcmds.h"
#include "commands/portalcmds.h" #include "commands/portalcmds.h"
#include "commands/prepare.h" #include "commands/prepare.h"
...@@ -210,6 +211,7 @@ check_xact_readonly(Node *parsetree) ...@@ -210,6 +211,7 @@ check_xact_readonly(Node *parsetree)
case T_ReassignOwnedStmt: case T_ReassignOwnedStmt:
case T_AlterTSDictionaryStmt: case T_AlterTSDictionaryStmt:
case T_AlterTSConfigurationStmt: case T_AlterTSConfigurationStmt:
case T_CreateExtensionStmt:
case T_CreateFdwStmt: case T_CreateFdwStmt:
case T_AlterFdwStmt: case T_AlterFdwStmt:
case T_DropFdwStmt: case T_DropFdwStmt:
...@@ -594,6 +596,10 @@ standard_ProcessUtility(Node *parsetree, ...@@ -594,6 +596,10 @@ standard_ProcessUtility(Node *parsetree,
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree); AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree);
break; break;
case T_CreateExtensionStmt:
CreateExtension((CreateExtensionStmt *) parsetree);
break;
case T_CreateFdwStmt: case T_CreateFdwStmt:
CreateForeignDataWrapper((CreateFdwStmt *) parsetree); CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
break; break;
...@@ -673,6 +679,10 @@ standard_ProcessUtility(Node *parsetree, ...@@ -673,6 +679,10 @@ standard_ProcessUtility(Node *parsetree,
RemoveTSConfigurations(stmt); RemoveTSConfigurations(stmt);
break; break;
case OBJECT_EXTENSION:
RemoveExtensions(stmt);
break;
default: default:
elog(ERROR, "unrecognized drop object type: %d", elog(ERROR, "unrecognized drop object type: %d",
(int) stmt->removeType); (int) stmt->removeType);
...@@ -1544,6 +1554,10 @@ CreateCommandTag(Node *parsetree) ...@@ -1544,6 +1554,10 @@ CreateCommandTag(Node *parsetree)
tag = "ALTER TABLESPACE"; tag = "ALTER TABLESPACE";
break; break;
case T_CreateExtensionStmt:
tag = "CREATE EXTENSION";
break;
case T_CreateFdwStmt: case T_CreateFdwStmt:
tag = "CREATE FOREIGN DATA WRAPPER"; tag = "CREATE FOREIGN DATA WRAPPER";
break; break;
...@@ -1626,6 +1640,9 @@ CreateCommandTag(Node *parsetree) ...@@ -1626,6 +1640,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_FOREIGN_TABLE: case OBJECT_FOREIGN_TABLE:
tag = "DROP FOREIGN TABLE"; tag = "DROP FOREIGN TABLE";
break; break;
case OBJECT_EXTENSION:
tag = "DROP EXTENSION";
break;
default: default:
tag = "???"; tag = "???";
} }
...@@ -1741,6 +1758,9 @@ CreateCommandTag(Node *parsetree) ...@@ -1741,6 +1758,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_DOMAIN: case OBJECT_DOMAIN:
tag = "ALTER DOMAIN"; tag = "ALTER DOMAIN";
break; break;
case OBJECT_EXTENSION:
tag = "ALTER EXTENSION";
break;
case OBJECT_OPERATOR: case OBJECT_OPERATOR:
tag = "ALTER OPERATOR"; tag = "ALTER OPERATOR";
break; break;
...@@ -2382,6 +2402,10 @@ GetCommandLogLevel(Node *parsetree) ...@@ -2382,6 +2402,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL; lev = LOGSTMT_DDL;
break; break;
case T_CreateExtensionStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateFdwStmt: case T_CreateFdwStmt:
case T_AlterFdwStmt: case T_AlterFdwStmt:
case T_DropFdwStmt: case T_DropFdwStmt:
......
...@@ -82,22 +82,16 @@ convert_and_check_filename(text *arg) ...@@ -82,22 +82,16 @@ convert_and_check_filename(text *arg)
/* /*
* Read a section of a file, returning it as bytea * Read a section of a file, returning it as bytea
* *
* We read the whole of the file when bytes_to_read is nagative. * Caller is responsible for all permissions checking.
*
* We read the whole of the file when bytes_to_read is negative.
*/ */
static bytea * bytea *
read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read) read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
{ {
bytea *buf; bytea *buf;
size_t nbytes; size_t nbytes;
FILE *file; FILE *file;
char *filename;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to read files"))));
filename = convert_and_check_filename(filename_t);
if (bytes_to_read < 0) if (bytes_to_read < 0)
{ {
...@@ -146,7 +140,6 @@ read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read) ...@@ -146,7 +140,6 @@ read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read)
SET_VARSIZE(buf, nbytes + VARHDRSZ); SET_VARSIZE(buf, nbytes + VARHDRSZ);
FreeFile(file); FreeFile(file);
pfree(filename);
return buf; return buf;
} }
...@@ -156,9 +149,11 @@ read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read) ...@@ -156,9 +149,11 @@ read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read)
* in the database encoding. * in the database encoding.
*/ */
static text * static text *
read_text_file(text *filename, int64 seek_offset, int64 bytes_to_read) read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
{ {
bytea *buf = read_binary_file(filename, seek_offset, bytes_to_read); bytea *buf;
buf = read_binary_file(filename, seek_offset, bytes_to_read);
/* Make sure the input is valid */ /* Make sure the input is valid */
pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false); pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false);
...@@ -176,13 +171,21 @@ pg_read_file(PG_FUNCTION_ARGS) ...@@ -176,13 +171,21 @@ pg_read_file(PG_FUNCTION_ARGS)
text *filename_t = PG_GETARG_TEXT_P(0); text *filename_t = PG_GETARG_TEXT_P(0);
int64 seek_offset = PG_GETARG_INT64(1); int64 seek_offset = PG_GETARG_INT64(1);
int64 bytes_to_read = PG_GETARG_INT64(2); int64 bytes_to_read = PG_GETARG_INT64(2);
char *filename;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to read files"))));
filename = convert_and_check_filename(filename_t);
if (bytes_to_read < 0) if (bytes_to_read < 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("requested length cannot be negative"))); errmsg("requested length cannot be negative")));
PG_RETURN_TEXT_P(read_text_file(filename_t, seek_offset, bytes_to_read)); PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read));
} }
/* /*
...@@ -192,8 +195,16 @@ Datum ...@@ -192,8 +195,16 @@ Datum
pg_read_file_all(PG_FUNCTION_ARGS) pg_read_file_all(PG_FUNCTION_ARGS)
{ {
text *filename_t = PG_GETARG_TEXT_P(0); text *filename_t = PG_GETARG_TEXT_P(0);
char *filename;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to read files"))));
filename = convert_and_check_filename(filename_t);
PG_RETURN_TEXT_P(read_text_file(filename_t, 0, -1)); PG_RETURN_TEXT_P(read_text_file(filename, 0, -1));
} }
/* /*
...@@ -205,13 +216,21 @@ pg_read_binary_file(PG_FUNCTION_ARGS) ...@@ -205,13 +216,21 @@ pg_read_binary_file(PG_FUNCTION_ARGS)
text *filename_t = PG_GETARG_TEXT_P(0); text *filename_t = PG_GETARG_TEXT_P(0);
int64 seek_offset = PG_GETARG_INT64(1); int64 seek_offset = PG_GETARG_INT64(1);
int64 bytes_to_read = PG_GETARG_INT64(2); int64 bytes_to_read = PG_GETARG_INT64(2);
char *filename;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to read files"))));
filename = convert_and_check_filename(filename_t);
if (bytes_to_read < 0) if (bytes_to_read < 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("requested length cannot be negative"))); errmsg("requested length cannot be negative")));
PG_RETURN_BYTEA_P(read_binary_file(filename_t, seek_offset, bytes_to_read)); PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read));
} }
/* /*
...@@ -221,8 +240,16 @@ Datum ...@@ -221,8 +240,16 @@ Datum
pg_read_binary_file_all(PG_FUNCTION_ARGS) pg_read_binary_file_all(PG_FUNCTION_ARGS)
{ {
text *filename_t = PG_GETARG_TEXT_P(0); text *filename_t = PG_GETARG_TEXT_P(0);
char *filename;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to read files"))));
filename = convert_and_check_filename(filename_t);
PG_RETURN_BYTEA_P(read_binary_file(filename_t, 0, -1)); PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1));
} }
/* /*
......
...@@ -84,6 +84,7 @@ getSchemaData(int *numTablesPtr) ...@@ -84,6 +84,7 @@ getSchemaData(int *numTablesPtr)
RuleInfo *ruleinfo; RuleInfo *ruleinfo;
ProcLangInfo *proclanginfo; ProcLangInfo *proclanginfo;
CastInfo *castinfo; CastInfo *castinfo;
ExtensionInfo *extinfo;
OpclassInfo *opcinfo; OpclassInfo *opcinfo;
OpfamilyInfo *opfinfo; OpfamilyInfo *opfinfo;
ConvInfo *convinfo; ConvInfo *convinfo;
...@@ -100,6 +101,7 @@ getSchemaData(int *numTablesPtr) ...@@ -100,6 +101,7 @@ getSchemaData(int *numTablesPtr)
int numRules; int numRules;
int numProcLangs; int numProcLangs;
int numCasts; int numCasts;
int numExtensions;
int numOpclasses; int numOpclasses;
int numOpfamilies; int numOpfamilies;
int numConversions; int numConversions;
...@@ -197,6 +199,11 @@ getSchemaData(int *numTablesPtr) ...@@ -197,6 +199,11 @@ getSchemaData(int *numTablesPtr)
write_msg(NULL, "reading type casts\n"); write_msg(NULL, "reading type casts\n");
castinfo = getCasts(&numCasts); castinfo = getCasts(&numCasts);
/* this must be after getTables */
if (g_verbose)
write_msg(NULL, "reading extensions\n");
extinfo = getExtensions(&numExtensions);
/* Link tables to parents, mark parents of target tables interesting */ /* Link tables to parents, mark parents of target tables interesting */
if (g_verbose) if (g_verbose)
write_msg(NULL, "finding inheritance relationships\n"); write_msg(NULL, "finding inheritance relationships\n");
......
This diff is collapsed.
...@@ -89,6 +89,7 @@ typedef enum ...@@ -89,6 +89,7 @@ typedef enum
{ {
/* When modifying this enum, update priority tables in pg_dump_sort.c! */ /* When modifying this enum, update priority tables in pg_dump_sort.c! */
DO_NAMESPACE, DO_NAMESPACE,
DO_EXTENSION,
DO_TYPE, DO_TYPE,
DO_SHELL_TYPE, DO_SHELL_TYPE,
DO_FUNC, DO_FUNC,
...@@ -139,6 +140,12 @@ typedef struct _namespaceInfo ...@@ -139,6 +140,12 @@ typedef struct _namespaceInfo
char *nspacl; char *nspacl;
} NamespaceInfo; } NamespaceInfo;
typedef struct _extensionInfo
{
DumpableObject dobj;
char *namespace; /* schema containing extension's objects */
} ExtensionInfo;
typedef struct _typeInfo typedef struct _typeInfo
{ {
DumpableObject dobj; DumpableObject dobj;
...@@ -288,6 +295,8 @@ typedef struct _tableDataInfo ...@@ -288,6 +295,8 @@ typedef struct _tableDataInfo
DumpableObject dobj; DumpableObject dobj;
TableInfo *tdtable; /* link to table to dump */ TableInfo *tdtable; /* link to table to dump */
bool oids; /* include OIDs in data? */ bool oids; /* include OIDs in data? */
bool ext_config; /* is table an extension config table? */
char *filtercond; /* WHERE condition to limit rows dumped */
} TableDataInfo; } TableDataInfo;
typedef struct _indxInfo typedef struct _indxInfo
...@@ -513,6 +522,7 @@ extern void sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs); ...@@ -513,6 +522,7 @@ extern void sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs);
* version specific routines * version specific routines
*/ */
extern NamespaceInfo *getNamespaces(int *numNamespaces); extern NamespaceInfo *getNamespaces(int *numNamespaces);
extern ExtensionInfo *getExtensions(int *numExtensions);
extern TypeInfo *getTypes(int *numTypes); extern TypeInfo *getTypes(int *numTypes);
extern FuncInfo *getFuncs(int *numFuncs); extern FuncInfo *getFuncs(int *numFuncs);
extern AggInfo *getAggregates(int *numAggregates); extern AggInfo *getAggregates(int *numAggregates);
......
This diff is collapsed.
...@@ -495,6 +495,12 @@ exec_command(const char *cmd, ...@@ -495,6 +495,12 @@ exec_command(const char *cmd,
break; break;
} }
break; break;
case 'x': /* Extensions */
if (show_verbose)
success = listExtensionContents(pattern);
else
success = listExtensions(pattern);
break;
default: default:
status = PSQL_CMD_UNKNOWN; status = PSQL_CMD_UNKNOWN;
} }
......
This diff is collapsed.
...@@ -87,4 +87,10 @@ extern bool listForeignTables(const char *pattern, bool verbose); ...@@ -87,4 +87,10 @@ extern bool listForeignTables(const char *pattern, bool verbose);
/* \dL */ /* \dL */
extern bool listLanguages(const char *pattern, bool verbose, bool showSystem); extern bool listLanguages(const char *pattern, bool verbose, bool showSystem);
/* \dx */
extern bool listExtensions(const char *pattern);
/* \dx+ */
extern bool listExtensionContents(const char *pattern);
#endif /* DESCRIBE_H */ #endif /* DESCRIBE_H */
...@@ -222,6 +222,7 @@ slashUsage(unsigned short int pager) ...@@ -222,6 +222,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\du[+] [PATTERN] list roles\n")); fprintf(output, _(" \\du[+] [PATTERN] list roles\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n")); fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n")); fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n"));
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
fprintf(output, _(" \\l[+] list all databases\n")); fprintf(output, _(" \\l[+] list all databases\n"));
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n")); fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
fprintf(output, _(" \\z [PATTERN] same as \\dp\n")); fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
......
This diff is collapsed.
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201102083 #define CATALOG_VERSION_NO 201102084
#endif #endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -23,5 +23,6 @@ extern void RenameConversion(List *name, const char *newname); ...@@ -23,5 +23,6 @@ extern void RenameConversion(List *name, const char *newname);
extern void AlterConversionOwner(List *name, Oid newOwnerId); extern void AlterConversionOwner(List *name, Oid newOwnerId);
extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId); extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
extern void AlterConversionNamespace(List *name, const char *newschema); extern void AlterConversionNamespace(List *name, const char *newschema);
extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
#endif /* CONVERSIONCMDS_H */ #endif /* CONVERSIONCMDS_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment