mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
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
This commit is contained in:
parent
414c5a2ea6
commit
d9572c4e3b
@ -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 @@
|
|||||||
<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 @@
|
|||||||
</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 @@
|
|||||||
</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 @@
|
|||||||
</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 @@
|
|||||||
</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>
|
||||||
|
|
||||||
|
@ -38,6 +38,11 @@
|
|||||||
operator classes for indexes (starting in <xref linkend="xindex">)
|
operator classes for indexes (starting in <xref linkend="xindex">)
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
packages of related objects (starting in <xref linkend="extend-extensions">)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -273,67 +278,701 @@
|
|||||||
&xoper;
|
&xoper;
|
||||||
&xindex;
|
&xindex;
|
||||||
|
|
||||||
<sect1 id="extend-Cpp">
|
|
||||||
<title>Using C++ for Extensibility</title>
|
|
||||||
|
|
||||||
<indexterm zone="extend-Cpp">
|
<sect1 id="extend-extensions">
|
||||||
<primary>C++</primary>
|
<title>Packaging Related Objects into an Extension</title>
|
||||||
|
|
||||||
|
<indexterm zone="extend-extensions">
|
||||||
|
<primary>extension</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
It is possible to use a compiler in C++ mode to build
|
A useful extension to <productname>PostgreSQL</> typically includes
|
||||||
<productname>PostgreSQL</productname> extensions by following these
|
multiple SQL objects; for example, a new datatype will require new
|
||||||
guidelines:
|
functions, new operators, and probably new index operator classes.
|
||||||
|
It is helpful to collect all these objects into a single package
|
||||||
|
to simplify database management. <productname>PostgreSQL</> calls
|
||||||
|
such a package an <firstterm>extension</>. To define an extension,
|
||||||
|
you need at least a <firstterm>script file</> that contains the
|
||||||
|
<acronym>SQL</> commands to create the extension's objects, and a
|
||||||
|
<firstterm>control file</> that specifies a few basic properties
|
||||||
|
of the extension itself. If the extension includes C code, there
|
||||||
|
will typically also be a shared library file into which the C code
|
||||||
|
has been built. Once you have these files, a simple
|
||||||
|
<xref linkend="sql-createextension"> command loads the objects into
|
||||||
|
your database.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The advantage of using an extension, rather than just running the
|
||||||
|
<acronym>SQL</> script to load a bunch of <quote>loose</> objects
|
||||||
|
into your database, is that <productname>PostgreSQL</> will then
|
||||||
|
understand that the objects of the extension go together. You can
|
||||||
|
drop all the objects with a single <xref linkend="sql-dropextension">
|
||||||
|
command (no need to maintain a separate <quote>uninstall</> script).
|
||||||
|
Even more useful, <application>pg_dump</> knows that it should not
|
||||||
|
dump the individual member objects of the extension — it will
|
||||||
|
just include a <command>CREATE EXTENSION</> command in dumps, instead.
|
||||||
|
This vastly simplifies migration to a new version of the extension
|
||||||
|
that might contain more or different objects than the old version.
|
||||||
|
Note however that you must have the extension's control, script, and
|
||||||
|
other files available when loading such a dump into a new database.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<productname>PostgreSQL</> will not let you drop an individual object
|
||||||
|
contained in an extension, except by dropping the whole extension.
|
||||||
|
Also, while you can change the definition of an extension member object
|
||||||
|
(for example, via <command>CREATE OR REPLACE FUNCTION</command> for a
|
||||||
|
function), bear in mind that the modified definition will not be dumped
|
||||||
|
by <application>pg_dump</>. Such a change is usually only sensible if
|
||||||
|
you concurrently make the same change in the extension's script file.
|
||||||
|
(But there are special provisions for tables containing configuration
|
||||||
|
data; see below.)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Extension Files</title>
|
||||||
|
|
||||||
|
<indexterm>
|
||||||
|
<primary>control file</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <xref linkend="sql-createextension"> command relies on a control
|
||||||
|
file for each extension, which must be named the same as the extension
|
||||||
|
with a suffix of <literal>.control</>, and must be placed in the
|
||||||
|
installation's <literal>SHAREDIR/contrib</literal> directory. There
|
||||||
|
must also be a <acronym>SQL</> script file, which typically is
|
||||||
|
named after the extension with a suffix of <literal>.sql</>, and is also
|
||||||
|
placed in the <literal>SHAREDIR/contrib</literal> directory; but these
|
||||||
|
defaults can be overridden by the control file.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The file format for an extension control file is the same as for the
|
||||||
|
<filename>postgresql.conf</> file, namely a list of
|
||||||
|
<replaceable>parameter-name</> <literal>=</> <replaceable>value</>
|
||||||
|
assignments, one per line. Blank lines and comments introduced by
|
||||||
|
<literal>#</> are allowed. Be sure to quote any value that is not
|
||||||
|
a single word or number.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
A control file can set the following parameters:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>script</varname> (<type>string</type>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The filename of the extension's <acronym>SQL</> script.
|
||||||
|
Defaults to the same name as the control file, but with the
|
||||||
|
<literal>.sql</literal> extension. Unless an absolute path is
|
||||||
|
given, the name is relative to the <literal>SHAREDIR/contrib</literal>
|
||||||
|
directory.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>version</varname> (<type>string</type>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The version of the extension. Any string can be given.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>comment</varname> (<type>string</type>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A comment (any string) about the extension. Alternatively,
|
||||||
|
the comment can be set by means of the <xref linkend="sql-comment">
|
||||||
|
command.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>requires</varname> (<type>string</type>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A list of names of extensions that this extension depends on,
|
||||||
|
for example <literal>requires = 'foo, bar'</literal>. Those
|
||||||
|
extensions must be installed before this one can be installed.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>encoding</varname> (<type>string</type>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The character set encoding used by the script file. This should
|
||||||
|
be specified if the script file contains any non-ASCII characters.
|
||||||
|
Otherwise the script will be assumed to be in the encoding of the
|
||||||
|
database it is loaded into.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>relocatable</varname> (<type>boolean</type>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
An extension is <firstterm>relocatable</> if it is possible to move
|
||||||
|
its contained objects into a different schema after initial creation
|
||||||
|
of the extension. The default is <literal>false</>, i.e. the
|
||||||
|
extension is not relocatable.
|
||||||
|
See below for more information.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>schema</varname> (<type>string</type>)</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
This parameter can only be set for non-relocatable extensions.
|
||||||
|
It forces the extension to be loaded into exactly the named schema
|
||||||
|
and not any other. See below for more information.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
An extension's <acronym>SQL</> script file can contain any SQL commands,
|
||||||
|
except for transaction control commands (<command>BEGIN</>,
|
||||||
|
<command>COMMIT</>, etc) and commands that cannot be executed inside a
|
||||||
|
transaction block (such as <command>VACUUM</>). This is because the
|
||||||
|
script file is implicitly executed within a transaction block.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
While the script file can contain any characters allowed by the specified
|
||||||
|
encoding, the control file should contain only plain ASCII, because there
|
||||||
|
is no way for <productname>PostgreSQL</> to know what encoding the
|
||||||
|
control file is in. In practice this is only an issue if you want to
|
||||||
|
use non-ASCII characters in the extension's comment. Recommended
|
||||||
|
practice in that case is to not use the <varname>comment</> parameter
|
||||||
|
in the control file, but instead use <command>COMMENT ON EXTENSION</>
|
||||||
|
within the script file to set the comment.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Extension Relocatability</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Users often wish to load the objects contained in an extension into a
|
||||||
|
different schema than the extension's author had in mind. There are
|
||||||
|
three supported levels of relocatability:
|
||||||
|
</para>
|
||||||
|
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
All functions accessed by the backend must present a C interface
|
A fully relocatable extension can be moved into another schema
|
||||||
to the backend; these C functions can then call C++ functions.
|
at any time, even after it's been loaded into a database.
|
||||||
For example, <literal>extern C</> linkage is required for
|
This is done with the <command>ALTER EXTENSION SET SCHEMA</>
|
||||||
backend-accessed functions. This is also necessary for any
|
command, which automatically renames all the member objects into
|
||||||
functions that are passed as pointers between the backend and
|
the new schema. Normally, this is only possible if the extension
|
||||||
C++ code.
|
contains no internal assumptions about what schema any of its
|
||||||
|
objects are in. Also, the extension's objects must all be in one
|
||||||
|
schema to begin with (ignoring objects that do not belong to any
|
||||||
|
schema, such as procedural languages). Mark a fully relocatable
|
||||||
|
extension by setting <literal>relocatable = true</> in its control
|
||||||
|
file.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Free memory using the appropriate deallocation method. For example,
|
An extension might be relocatable during installation but not
|
||||||
most backend memory is allocated using <function>palloc()</>, so use
|
afterwards. This is typically the case if the extension's script
|
||||||
<function>pfree()</> to free it, i.e. using C++
|
file needs to reference the target schema explicitly, for example
|
||||||
<function>delete()</> in such cases will fail.
|
in setting <literal>search_path</> properties for SQL functions.
|
||||||
|
For such an extension, set <literal>relocatable = false</> in its
|
||||||
|
control file, and use <literal>@extschema@</> to refer to the target
|
||||||
|
schema in the script file. All occurrences of this string will be
|
||||||
|
replaced by the actual target schema's name before the script is
|
||||||
|
executed. The user can set the target schema using the
|
||||||
|
<literal>SCHEMA</> option of <command>CREATE EXTENSION</>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Prevent exceptions from propagating into the C code (use a
|
If the extension does not support relocation at all, set
|
||||||
catch-all block at the top level of all <literal>extern C</>
|
<literal>relocatable = false</> in its control file, and also set
|
||||||
functions). This is necessary even if the C++ code does not
|
<literal>schema</> to the name of the intended target schema. This
|
||||||
throw any exceptions because events like out-of-memory still
|
will prevent use of the <literal>SCHEMA</> option of <command>CREATE
|
||||||
throw exceptions. Any exceptions must be caught and appropriate
|
EXTENSION</>, unless it specifies the same schema named in the control
|
||||||
errors passed back to the C interface. If possible, compile C++
|
file. This choice is typically necessary if the extension contains
|
||||||
with <option>-fno-exceptions</> to eliminate exceptions entirely;
|
internal assumptions about schema names that can't be replaced by
|
||||||
in such cases, you must check for failures in your C++ code, e.g.
|
uses of <literal>@extschema@</>. The <literal>@extschema@</>
|
||||||
check for NULL returned by <function>new()</>.
|
substitution mechanism is available in this case too, although it is
|
||||||
</para>
|
of limited use since the schema name is determined by the control file.
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
If calling backend functions from C++ code, be sure that the
|
|
||||||
C++ call stack contains only plain old data structures
|
|
||||||
(<acronym>POD</>). This is necessary because backend errors
|
|
||||||
generate a distant <function>longjmp()</> that does not properly
|
|
||||||
unroll a C++ call stack with non-POD objects.
|
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
In all cases, the script file will be executed with
|
||||||
|
<xref linkend="guc-search-path"> initially set to point to the target
|
||||||
|
schema; that is, <command>CREATE EXTENSION</> does the equivalent of
|
||||||
|
this:
|
||||||
|
<programlisting>
|
||||||
|
SET LOCAL search_path TO @extschema@;
|
||||||
|
</programlisting>
|
||||||
|
This allows the objects created by the script file to go into the target
|
||||||
|
schema. The script file can change <varname>search_path</> if it wishes,
|
||||||
|
but that is generally undesirable. <varname>search_path</> is restored
|
||||||
|
to its previous setting upon completion of <command>CREATE EXTENSION</>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The target schema is determined by the <varname>schema</> parameter in
|
||||||
|
the control file if that is given, otherwise by the <literal>SCHEMA</>
|
||||||
|
option of <command>CREATE EXTENSION</> if that is given, otherwise the
|
||||||
|
current default object creation schema (the first one in the caller's
|
||||||
|
<varname>search_path</>). When the control file <varname>schema</>
|
||||||
|
parameter is used, the target schema will be created if it doesn't
|
||||||
|
already exist, but in the other two cases it must already exist.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
If any prerequisite extensions are listed in <varname>requires</varname>
|
||||||
|
in the control file, their target schemas are appended to the initial
|
||||||
|
setting of <varname>search_path</>. This allows their objects to be
|
||||||
|
visible to the new extension's script file.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Although a non-relocatable extension can contain objects spread across
|
||||||
|
multiple schemas, it is usually desirable to place all the objects meant
|
||||||
|
for external use into a single schema, which is considered the extension's
|
||||||
|
target schema. Such an arrangement works conveniently with the default
|
||||||
|
setting of <varname>search_path</> during creation of dependent
|
||||||
|
extensions.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Extension Configuration Tables</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Some extensions include configuration tables, which contain data that
|
||||||
|
might be added or changed by the user after installation of the
|
||||||
|
extension. Ordinarily, if a table is part of an extension, neither
|
||||||
|
the table's definition nor its content will be dumped by
|
||||||
|
<application>pg_dump</>. But that behavior is undesirable for a
|
||||||
|
configuration table; any data changes made by the user need to be
|
||||||
|
included in dumps, or the extension will behave differently after a dump
|
||||||
|
and reload.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
To solve this problem, an extension's script file can mark a table
|
||||||
|
it has created as a configuration table, which will cause
|
||||||
|
<application>pg_dump</> to include the table's contents (not its
|
||||||
|
definition) in dumps. To do that, call the function
|
||||||
|
<function>pg_extension_config_dump(regclass, text)</> after creating the
|
||||||
|
table, for example
|
||||||
|
<programlisting>
|
||||||
|
CREATE TABLE my_config (key text, value text);
|
||||||
|
|
||||||
|
SELECT pg_catalog.pg_extension_config_dump('my_config', '');
|
||||||
|
</programlisting>
|
||||||
|
Any number of tables can be marked this way.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When the second argument of <function>pg_extension_config_dump</> is
|
||||||
|
an empty string, the entire contents of the table are dumped by
|
||||||
|
<application>pg_dump</>. This is usually only correct if the table
|
||||||
|
is initially empty as created by the extension script. If there is
|
||||||
|
a mixture of initial data and user-provided data in the table,
|
||||||
|
the second argument of <function>pg_extension_config_dump</> provides
|
||||||
|
a <literal>WHERE</> condition that selects the data to be dumped.
|
||||||
|
For example, you might do
|
||||||
|
<programlisting>
|
||||||
|
CREATE TABLE my_config (key text, value text, standard_entry boolean);
|
||||||
|
|
||||||
|
SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');
|
||||||
|
</programlisting>
|
||||||
|
and then make sure that <structfield>standard_entry</> is true only
|
||||||
|
in the rows created by the extension's script.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
More complicated situations, such as initially-provided rows that might
|
||||||
|
be modified by users, can be handled by creating triggers on the
|
||||||
|
configuration table to ensure that modified rows are marked correctly.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Extension Example</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Here is a complete example of an <acronym>SQL</>-only
|
||||||
|
extension, a two-element composite type that can store any type of value
|
||||||
|
in its slots, which are named <quote>k</> and <quote>v</>. Non-text
|
||||||
|
values are automatically coerced to text for storage.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The script file <filename>pair.sql</> looks like this:
|
||||||
|
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
CREATE TYPE pair AS ( k text, v text );
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION pair(anyelement, text)
|
||||||
|
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION pair(text, anyelement)
|
||||||
|
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION pair(anyelement, anyelement)
|
||||||
|
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair';
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION pair(text, text)
|
||||||
|
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;';
|
||||||
|
|
||||||
|
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = anyelement, PROCEDURE = pair);
|
||||||
|
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = text, PROCEDURE = pair);
|
||||||
|
CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = anyelement, PROCEDURE = pair);
|
||||||
|
CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair);
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The control file <filename>pair.control</> looks like this:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
# pair extension
|
||||||
|
comment = 'A key/value pair data type'
|
||||||
|
version = '0.1.2'
|
||||||
|
relocatable = true
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
While you hardly need a makefile to install these two files into the
|
||||||
|
correct directory, you could use a <filename>Makefile</> containing this:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
EXTENSION = pair
|
||||||
|
DATA = pair.sql
|
||||||
|
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
This makefile relies on <acronym>PGXS</acronym>, which is described
|
||||||
|
in <xref linkend="extend-pgxs">. The command <literal>make install</>
|
||||||
|
will then install the control and script files into the correct
|
||||||
|
directory as reported by <application>pg_config</>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Once the files are installed, use the
|
||||||
|
<xref linkend="sql-createextension"> command to load the objects into
|
||||||
|
any particular database.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="extend-pgxs">
|
||||||
|
<title>Extension Building Infrastructure</title>
|
||||||
|
|
||||||
|
<indexterm zone="extend-pgxs">
|
||||||
|
<primary>pgxs</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
If you are thinking about distributing your
|
||||||
|
<productname>PostgreSQL</> extension modules, setting up a
|
||||||
|
portable build system for them can be fairly difficult. Therefore
|
||||||
|
the <productname>PostgreSQL</> installation provides a build
|
||||||
|
infrastructure for extensions, called <acronym>PGXS</acronym>, so
|
||||||
|
that simple extension modules can be built simply against an
|
||||||
|
already installed server. <acronym>PGXS</acronym> is mainly intended
|
||||||
|
for extensions that include C code, although it can be used for
|
||||||
|
pure-SQL extensions too. Note that <acronym>PGXS</acronym> is not
|
||||||
|
intended to be a universal build system framework that can be used
|
||||||
|
to build any software interfacing to <productname>PostgreSQL</>;
|
||||||
|
it simply automates common build rules for simple server extension
|
||||||
|
modules. For more complicated packages, you might need to write your
|
||||||
|
own build system.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
In summary, it is best to place C++ code behind a wall of
|
To use the <acronym>PGXS</acronym> infrastructure for your extension,
|
||||||
<literal>extern C</> functions that interface to the backend,
|
you must write a simple makefile.
|
||||||
and avoid exception, memory, and call stack leakage.
|
In the makefile, you need to set some variables
|
||||||
|
and finally include the global <acronym>PGXS</acronym> makefile.
|
||||||
|
Here is an example that builds an extension module named
|
||||||
|
<literal>isbn_issn</literal>, consisting of a shared library containing
|
||||||
|
some C code, an extension control file, a SQL script, and a documentation
|
||||||
|
text file:
|
||||||
|
<programlisting>
|
||||||
|
MODULES = isbn_issn
|
||||||
|
EXTENSION = isbn_issn
|
||||||
|
DATA_built = isbn_issn.sql
|
||||||
|
DOCS = README.isbn_issn
|
||||||
|
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
</programlisting>
|
||||||
|
The last three lines should always be the same. Earlier in the
|
||||||
|
file, you assign variables or add custom
|
||||||
|
<application>make</application> rules.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Set one of these three variables to specify what is built:
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>MODULES</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
list of shared-library objects to be built from source files with same
|
||||||
|
stem (do not include library suffixes in this list)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>MODULE_big</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
a shared library to build from multiple source files
|
||||||
|
(list object files in <varname>OBJS</varname>)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>PROGRAM</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
an executable program to build
|
||||||
|
(list object files in <varname>OBJS</varname>)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
|
||||||
|
The following variables can also be set:
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>MODULEDIR</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
subdirectory into which EXTENSION, DATA and DOCS files should be
|
||||||
|
installed (if not set, default is <literal>contrib</literal>)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>EXTENSION</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
extension name(s); for each name you must provide an
|
||||||
|
<literal><replaceable>extension</replaceable>.control</literal> file,
|
||||||
|
which will be installed into
|
||||||
|
<literal><replaceable>prefix</replaceable>/share/$MODULEDIR</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>DATA</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
random files to install into <literal><replaceable>prefix</replaceable>/share/$MODULEDIR</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>DATA_built</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
random files to install into
|
||||||
|
<literal><replaceable>prefix</replaceable>/share/$MODULEDIR</literal>,
|
||||||
|
which need to be built first
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>DATA_TSEARCH</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
random files to install under
|
||||||
|
<literal><replaceable>prefix</replaceable>/share/tsearch_data</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>DOCS</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
random files to install under
|
||||||
|
<literal><replaceable>prefix</replaceable>/doc/$MODULEDIR</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>SCRIPTS</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
script files (not binaries) to install into
|
||||||
|
<literal><replaceable>prefix</replaceable>/bin</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>SCRIPTS_built</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
script files (not binaries) to install into
|
||||||
|
<literal><replaceable>prefix</replaceable>/bin</literal>,
|
||||||
|
which need to be built first
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>REGRESS</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
list of regression test cases (without suffix), see below
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>EXTRA_CLEAN</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
extra files to remove in <literal>make clean</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>PG_CPPFLAGS</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
will be added to <varname>CPPFLAGS</varname>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>PG_LIBS</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
will be added to <varname>PROGRAM</varname> link line
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>SHLIB_LINK</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
will be added to <varname>MODULE_big</varname> link line
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>PG_CONFIG</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
path to <application>pg_config</> program for the
|
||||||
|
<productname>PostgreSQL</productname> installation to build against
|
||||||
|
(typically just <literal>pg_config</> to use the first one in your
|
||||||
|
<varname>PATH</>)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Put this makefile as <literal>Makefile</literal> in the directory
|
||||||
|
which holds your extension. Then you can do
|
||||||
|
<literal>make</literal> to compile, and then <literal>make
|
||||||
|
install</literal> to install your module. By default, the extension is
|
||||||
|
compiled and installed for the
|
||||||
|
<productname>PostgreSQL</productname> installation that
|
||||||
|
corresponds to the first <command>pg_config</command> program
|
||||||
|
found in your <varname>PATH</>. You can use a different installation by
|
||||||
|
setting <varname>PG_CONFIG</varname> to point to its
|
||||||
|
<command>pg_config</command> program, either within the makefile
|
||||||
|
or on the <literal>make</literal> command line.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<caution>
|
||||||
|
<para>
|
||||||
|
Changing <varname>PG_CONFIG</varname> only works when building
|
||||||
|
against <productname>PostgreSQL</productname> 8.3 or later.
|
||||||
|
With older releases it does not work to set it to anything except
|
||||||
|
<literal>pg_config</>; you must alter your <varname>PATH</>
|
||||||
|
to select the installation to build against.
|
||||||
|
</para>
|
||||||
|
</caution>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The scripts listed in the <varname>REGRESS</> variable are used for
|
||||||
|
regression testing of your module, which can be invoked by <literal>make
|
||||||
|
installcheck</literal> after doing <literal>make install</>. For this to
|
||||||
|
work you must have a running <productname>PostgreSQL</productname> server.
|
||||||
|
The script files listed in <varname>REGRESS</> must appear in a
|
||||||
|
subdirectory named <literal>sql/</literal> in your extension's directory.
|
||||||
|
These files must have extension <literal>.sql</literal>, which must not be
|
||||||
|
included in the <varname>REGRESS</varname> list in the makefile. For each
|
||||||
|
test there should also be a file containing the expected output in a
|
||||||
|
subdirectory named <literal>expected/</literal>, with the same stem and
|
||||||
|
extension <literal>.out</literal>. <literal>make installcheck</literal>
|
||||||
|
executes each test script with <application>psql</>, and compares the
|
||||||
|
resulting output to the matching expected file. Any differences will be
|
||||||
|
written to the file <literal>regression.diffs</literal> in <command>diff
|
||||||
|
-c</command> format. Note that trying to run a test that is missing its
|
||||||
|
expected file will be reported as <quote>trouble</quote>, so make sure you
|
||||||
|
have all expected files.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<tip>
|
||||||
|
<para>
|
||||||
|
The easiest way to create the expected files is to create empty files,
|
||||||
|
then do a test run (which will of course report differences). Inspect
|
||||||
|
the actual result files found in the <literal>results/</literal>
|
||||||
|
directory, then copy them to <literal>expected/</literal> if they match
|
||||||
|
what you expect from the test.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</tip>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
</chapter>
|
</chapter>
|
||||||
|
@ -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.
|
|||||||
<!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.
|
|||||||
<!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">
|
||||||
|
98
doc/src/sgml/ref/alter_extension.sgml
Normal file
98
doc/src/sgml/ref/alter_extension.sgml
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<!--
|
||||||
|
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
|
|||||||
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> |
|
||||||
|
118
doc/src/sgml/ref/create_extension.sgml
Normal file
118
doc/src/sgml/ref/create_extension.sgml
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<!--
|
||||||
|
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>
|
121
doc/src/sgml/ref/drop_extension.sgml
Normal file
121
doc/src/sgml/ref/drop_extension.sgml
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<!--
|
||||||
|
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=>
|
|||||||
</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=>
|
|||||||
</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 @@
|
|||||||
&alterDatabase;
|
&alterDatabase;
|
||||||
&alterDefaultPrivileges;
|
&alterDefaultPrivileges;
|
||||||
&alterDomain;
|
&alterDomain;
|
||||||
|
&alterExtension;
|
||||||
&alterForeignDataWrapper;
|
&alterForeignDataWrapper;
|
||||||
&alterForeignTable;
|
&alterForeignTable;
|
||||||
&alterFunction;
|
&alterFunction;
|
||||||
@ -78,6 +79,7 @@
|
|||||||
&createConversion;
|
&createConversion;
|
||||||
&createDatabase;
|
&createDatabase;
|
||||||
&createDomain;
|
&createDomain;
|
||||||
|
&createExtension;
|
||||||
&createForeignDataWrapper;
|
&createForeignDataWrapper;
|
||||||
&createForeignTable;
|
&createForeignTable;
|
||||||
&createFunction;
|
&createFunction;
|
||||||
@ -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
|
|||||||
|
|
||||||
<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>
|
||||||
|
|
||||||
|
@ -2392,273 +2392,6 @@ concat_text(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
&dfunc;
|
&dfunc;
|
||||||
|
|
||||||
<sect2 id="xfunc-c-pgxs">
|
|
||||||
<title>Extension Building Infrastructure</title>
|
|
||||||
|
|
||||||
<indexterm zone="xfunc-c-pgxs">
|
|
||||||
<primary>pgxs</primary>
|
|
||||||
</indexterm>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
If you are thinking about distributing your
|
|
||||||
<productname>PostgreSQL</> extension modules, setting up a
|
|
||||||
portable build system for them can be fairly difficult. Therefore
|
|
||||||
the <productname>PostgreSQL</> installation provides a build
|
|
||||||
infrastructure for extensions, called <acronym>PGXS</acronym>, so
|
|
||||||
that simple extension modules can be built simply against an
|
|
||||||
already installed server. Note that this infrastructure is not
|
|
||||||
intended to be a universal build system framework that can be used
|
|
||||||
to build all software interfacing to <productname>PostgreSQL</>;
|
|
||||||
it simply automates common build rules for simple server extension
|
|
||||||
modules. For more complicated packages, you need to write your
|
|
||||||
own build system.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
To use the infrastructure for your extension, you must write a
|
|
||||||
simple makefile. In that makefile, you need to set some variables
|
|
||||||
and finally include the global <acronym>PGXS</acronym> makefile.
|
|
||||||
Here is an example that builds an extension module named
|
|
||||||
<literal>isbn_issn</literal> consisting of a shared library, an
|
|
||||||
SQL script, and a documentation text file:
|
|
||||||
<programlisting>
|
|
||||||
MODULES = isbn_issn
|
|
||||||
DATA_built = isbn_issn.sql
|
|
||||||
DOCS = README.isbn_issn
|
|
||||||
|
|
||||||
PG_CONFIG = pg_config
|
|
||||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
|
||||||
include $(PGXS)
|
|
||||||
</programlisting>
|
|
||||||
The last three lines should always be the same. Earlier in the
|
|
||||||
file, you assign variables or add custom
|
|
||||||
<application>make</application> rules.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Set one of these three variables to specify what is built:
|
|
||||||
|
|
||||||
<variablelist>
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>MODULES</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
list of shared objects to be built from source files with same
|
|
||||||
stem (do not include suffix in this list)
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>MODULE_big</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
a shared object to build from multiple source files
|
|
||||||
(list object files in <varname>OBJS</varname>)
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>PROGRAM</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
a binary program to build
|
|
||||||
(list object files in <varname>OBJS</varname>)
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
</variablelist>
|
|
||||||
|
|
||||||
The following variables can also be set:
|
|
||||||
|
|
||||||
<variablelist>
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>MODULEDIR</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
subdirectory into which DATA and DOCS files should be
|
|
||||||
installed (if not set, default is <literal>contrib</literal>)
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>DATA</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
random files to install into <literal><replaceable>prefix</replaceable>/share/$MODULEDIR</literal>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>DATA_built</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
random files to install into
|
|
||||||
<literal><replaceable>prefix</replaceable>/share/$MODULEDIR</literal>,
|
|
||||||
which need to be built first
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>DATA_TSEARCH</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
random files to install under
|
|
||||||
<literal><replaceable>prefix</replaceable>/share/tsearch_data</literal>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>DOCS</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
random files to install under
|
|
||||||
<literal><replaceable>prefix</replaceable>/doc/$MODULEDIR</literal>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>SCRIPTS</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
script files (not binaries) to install into
|
|
||||||
<literal><replaceable>prefix</replaceable>/bin</literal>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>SCRIPTS_built</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
script files (not binaries) to install into
|
|
||||||
<literal><replaceable>prefix</replaceable>/bin</literal>,
|
|
||||||
which need to be built first
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>REGRESS</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
list of regression test cases (without suffix), see below
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>EXTRA_CLEAN</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
extra files to remove in <literal>make clean</literal>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>PG_CPPFLAGS</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
will be added to <varname>CPPFLAGS</varname>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>PG_LIBS</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
will be added to <varname>PROGRAM</varname> link line
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>SHLIB_LINK</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
will be added to <varname>MODULE_big</varname> link line
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>PG_CONFIG</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
path to <application>pg_config</> program for the
|
|
||||||
<productname>PostgreSQL</productname> installation to build against
|
|
||||||
(typically just <literal>pg_config</> to use the first one in your
|
|
||||||
<varname>PATH</>)
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
</variablelist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Put this makefile as <literal>Makefile</literal> in the directory
|
|
||||||
which holds your extension. Then you can do
|
|
||||||
<literal>make</literal> to compile, and later <literal>make
|
|
||||||
install</literal> to install your module. By default, the extension is
|
|
||||||
compiled and installed for the
|
|
||||||
<productname>PostgreSQL</productname> installation that
|
|
||||||
corresponds to the first <command>pg_config</command> program
|
|
||||||
found in your path. You can use a different installation by
|
|
||||||
setting <varname>PG_CONFIG</varname> to point to its
|
|
||||||
<command>pg_config</command> program, either within the makefile
|
|
||||||
or on the <literal>make</literal> command line.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<caution>
|
|
||||||
<para>
|
|
||||||
Changing <varname>PG_CONFIG</varname> only works when building
|
|
||||||
against <productname>PostgreSQL</productname> 8.3 or later.
|
|
||||||
With older releases it does not work to set it to anything except
|
|
||||||
<literal>pg_config</>; you must alter your <varname>PATH</>
|
|
||||||
to select the installation to build against.
|
|
||||||
</para>
|
|
||||||
</caution>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The scripts listed in the <varname>REGRESS</> variable are used for
|
|
||||||
regression testing of your module, just like <literal>make
|
|
||||||
installcheck</literal> is used for the main
|
|
||||||
<productname>PostgreSQL</productname> server. For this to work you need
|
|
||||||
to have a subdirectory named <literal>sql/</literal> in your extension's
|
|
||||||
directory, within which you put one file for each group of tests you want
|
|
||||||
to run. The files should have extension <literal>.sql</literal>, which
|
|
||||||
should not be included in the <varname>REGRESS</varname> list in the
|
|
||||||
makefile. For each test there should be a file containing the expected
|
|
||||||
result in a subdirectory named <literal>expected/</literal>, with extension
|
|
||||||
<literal>.out</literal>. The tests are run by executing <literal>make
|
|
||||||
installcheck</literal>, and the resulting output will be compared to the
|
|
||||||
expected files. The differences will be written to the file
|
|
||||||
<literal>regression.diffs</literal> in <command>diff -c</command> format.
|
|
||||||
Note that trying to run a test which is missing the expected file will be
|
|
||||||
reported as <quote>trouble</quote>, so make sure you have all expected
|
|
||||||
files.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<tip>
|
|
||||||
<para>
|
|
||||||
The easiest way of creating the expected files is creating empty files,
|
|
||||||
then carefully inspecting the result files after a test run (to be found
|
|
||||||
in the <literal>results/</literal> directory), and copying them to
|
|
||||||
<literal>expected/</literal> if they match what you want from the test.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</tip>
|
|
||||||
</sect2>
|
|
||||||
|
|
||||||
|
|
||||||
<sect2>
|
<sect2>
|
||||||
<title>Composite-type Arguments</title>
|
<title>Composite-type Arguments</title>
|
||||||
|
|
||||||
@ -3385,4 +3118,68 @@ if (!ptr)
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="extend-Cpp">
|
||||||
|
<title>Using C++ for Extensibility</title>
|
||||||
|
|
||||||
|
<indexterm zone="extend-Cpp">
|
||||||
|
<primary>C++</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Although the <productname>PostgreSQL</productname> backend is written in
|
||||||
|
C, it is possible to write extensions in C++ if these guidelines are
|
||||||
|
followed:
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
All functions accessed by the backend must present a C interface
|
||||||
|
to the backend; these C functions can then call C++ functions.
|
||||||
|
For example, <literal>extern C</> linkage is required for
|
||||||
|
backend-accessed functions. This is also necessary for any
|
||||||
|
functions that are passed as pointers between the backend and
|
||||||
|
C++ code.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Free memory using the appropriate deallocation method. For example,
|
||||||
|
most backend memory is allocated using <function>palloc()</>, so use
|
||||||
|
<function>pfree()</> to free it. Using C++
|
||||||
|
<function>delete</> in such cases will fail.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Prevent exceptions from propagating into the C code (use a catch-all
|
||||||
|
block at the top level of all <literal>extern C</> functions). This
|
||||||
|
is necessary even if the C++ code does not explicitly throw any
|
||||||
|
exceptions, because events like out-of-memory can still throw
|
||||||
|
exceptions. Any exceptions must be caught and appropriate errors
|
||||||
|
passed back to the C interface. If possible, compile C++ with
|
||||||
|
<option>-fno-exceptions</> to eliminate exceptions entirely; in such
|
||||||
|
cases, you must check for failures in your C++ code, e.g. check for
|
||||||
|
NULL returned by <function>new()</>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
If calling backend functions from C++ code, be sure that the
|
||||||
|
C++ call stack contains only plain old data structures
|
||||||
|
(<acronym>POD</>). This is necessary because backend errors
|
||||||
|
generate a distant <function>longjmp()</> that does not properly
|
||||||
|
unroll a C++ call stack with non-POD objects.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
In summary, it is best to place C++ code behind a wall of
|
||||||
|
<literal>extern C</> functions that interface to the backend,
|
||||||
|
and avoid exception, memory, and call stack leakage.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
@ -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 @@
|
|||||||
#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 @@
|
|||||||
#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
|
|||||||
#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] = {
|
|||||||
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,
|
|||||||
/* 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,
|
|||||||
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,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
* 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,
|
|||||||
|
|
||||||
recordDependencyOnOwner(RelationRelationId, relid, ownerid);
|
recordDependencyOnOwner(RelationRelationId, relid, ownerid);
|
||||||
|
|
||||||
|
recordDependencyOnCurrentExtension(&myself);
|
||||||
|
|
||||||
if (reloftypeid)
|
if (reloftypeid)
|
||||||
{
|
{
|
||||||
referenced.classId = TypeRelationId;
|
referenced.classId = TypeRelationId;
|
||||||
|
@ -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 @@
|
|||||||
#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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
#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,
|
|||||||
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,
|
|||||||
* 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)
|
|||||||
|
|
||||||
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)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
/* 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,
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
*
|
*
|
||||||
* 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,
|
|||||||
|
|
||||||
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,
|
|||||||
* 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,
|
|||||||
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
|
|||||||
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
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
|||||||
/* 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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
(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)
|
|||||||
/* 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;
|
||||||
}
|
}
|
||||||
|
1401
src/backend/commands/extension.c
Normal file
1401
src/backend/commands/extension.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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)
|
|||||||
|
|
||||||
heap_freetuple(tuple);
|
heap_freetuple(tuple);
|
||||||
|
|
||||||
|
/* record dependencies */
|
||||||
|
myself.classId = ForeignDataWrapperRelationId;
|
||||||
|
myself.objectId = fdwId;
|
||||||
|
myself.objectSubId = 0;
|
||||||
|
|
||||||
if (fdwvalidator)
|
if (fdwvalidator)
|
||||||
{
|
{
|
||||||
ObjectAddress myself;
|
|
||||||
ObjectAddress referenced;
|
|
||||||
|
|
||||||
myself.classId = ForeignDataWrapperRelationId;
|
|
||||||
myself.objectId = fdwId;
|
|
||||||
myself.objectSubId = 0;
|
|
||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
(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,
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
/* 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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
/* 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,
|
|||||||
* 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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
/* 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)
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
/* 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,
|
|||||||
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,
|
|||||||
/* 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)
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
* 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,
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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)
|
|||||||
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_
|
|||||||
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_
|
|||||||
%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_
|
|||||||
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 :
|
|||||||
| CreateCastStmt
|
| CreateCastStmt
|
||||||
| CreateConversionStmt
|
| CreateConversionStmt
|
||||||
| CreateDomainStmt
|
| CreateDomainStmt
|
||||||
|
| CreateExtensionStmt
|
||||||
| CreateFdwStmt
|
| CreateFdwStmt
|
||||||
| CreateForeignServerStmt
|
| CreateForeignServerStmt
|
||||||
| CreateForeignTableStmt
|
| CreateForeignTableStmt
|
||||||
@ -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; }
|
|||||||
| 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:
|
|||||||
*
|
*
|
||||||
* 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:
|
|||||||
| 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:
|
|||||||
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:
|
|||||||
| EXCLUSIVE
|
| EXCLUSIVE
|
||||||
| EXECUTE
|
| EXECUTE
|
||||||
| EXPLAIN
|
| EXPLAIN
|
||||||
|
| EXTENSION
|
||||||
| EXTERNAL
|
| EXTERNAL
|
||||||
| FAMILY
|
| FAMILY
|
||||||
| FIRST_P
|
| FIRST_P
|
||||||
|
@ -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 @@
|
|||||||
#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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
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
|
|||||||
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;
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(read_text_file(filename_t, 0, -1));
|
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, 0, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -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
|
|||||||
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;
|
||||||
|
|
||||||
PG_RETURN_BYTEA_P(read_binary_file(filename_t, 0, -1));
|
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, 0, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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)
|
|||||||
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)
|
|||||||
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");
|
||||||
|
@ -161,6 +161,7 @@ static int findSecLabels(Archive *fout, Oid classoid, Oid objoid,
|
|||||||
static int collectSecLabels(Archive *fout, SecLabelItem **items);
|
static int collectSecLabels(Archive *fout, SecLabelItem **items);
|
||||||
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
|
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
|
||||||
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
|
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
|
||||||
|
static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
|
||||||
static void dumpType(Archive *fout, TypeInfo *tyinfo);
|
static void dumpType(Archive *fout, TypeInfo *tyinfo);
|
||||||
static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
|
static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
|
||||||
static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
|
static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
|
||||||
@ -204,6 +205,7 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
|
|||||||
static void getDependencies(void);
|
static void getDependencies(void);
|
||||||
static void getDomainConstraints(TypeInfo *tyinfo);
|
static void getDomainConstraints(TypeInfo *tyinfo);
|
||||||
static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
|
static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
|
||||||
|
static void makeTableDataInfo(TableInfo *tbinfo, bool oids);
|
||||||
static void getTableDataFKConstraints(void);
|
static void getTableDataFKConstraints(void);
|
||||||
static char *format_function_arguments(FuncInfo *finfo, char *funcargs);
|
static char *format_function_arguments(FuncInfo *finfo, char *funcargs);
|
||||||
static char *format_function_arguments_old(FuncInfo *finfo, int nallargs,
|
static char *format_function_arguments_old(FuncInfo *finfo, int nallargs,
|
||||||
@ -764,6 +766,9 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Collect dependency data to assist in ordering the objects.
|
* Collect dependency data to assist in ordering the objects.
|
||||||
|
*
|
||||||
|
* (In 9.1 and later, this also marks extension member objects as
|
||||||
|
* not to be dumped.)
|
||||||
*/
|
*/
|
||||||
getDependencies();
|
getDependencies();
|
||||||
|
|
||||||
@ -1232,6 +1237,23 @@ dumpTableData_copy(Archive *fout, void *dcontext)
|
|||||||
classname),
|
classname),
|
||||||
column_list);
|
column_list);
|
||||||
}
|
}
|
||||||
|
else if (tdinfo->filtercond)
|
||||||
|
{
|
||||||
|
/* Note: this syntax is only supported in 8.2 and up */
|
||||||
|
appendPQExpBufferStr(q, "COPY (SELECT ");
|
||||||
|
/* klugery to get rid of parens in column list */
|
||||||
|
if (strlen(column_list) > 2)
|
||||||
|
{
|
||||||
|
appendPQExpBufferStr(q, column_list + 1);
|
||||||
|
q->data[q->len - 1] = ' ';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
appendPQExpBufferStr(q, "* ");
|
||||||
|
appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
|
||||||
|
fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
|
||||||
|
classname),
|
||||||
|
tdinfo->filtercond);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(q, "COPY %s %s TO stdout;",
|
appendPQExpBuffer(q, "COPY %s %s TO stdout;",
|
||||||
@ -1356,6 +1378,8 @@ dumpTableData_insert(Archive *fout, void *dcontext)
|
|||||||
fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
|
fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
|
||||||
classname));
|
classname));
|
||||||
}
|
}
|
||||||
|
if (tdinfo->filtercond)
|
||||||
|
appendPQExpBuffer(q, " %s", tdinfo->filtercond);
|
||||||
|
|
||||||
res = PQexec(g_conn, q->data);
|
res = PQexec(g_conn, q->data);
|
||||||
check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK);
|
check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK);
|
||||||
@ -1480,10 +1504,15 @@ static void
|
|||||||
dumpTableData(Archive *fout, TableDataInfo *tdinfo)
|
dumpTableData(Archive *fout, TableDataInfo *tdinfo)
|
||||||
{
|
{
|
||||||
TableInfo *tbinfo = tdinfo->tdtable;
|
TableInfo *tbinfo = tdinfo->tdtable;
|
||||||
PQExpBuffer copyBuf = createPQExpBuffer();
|
PQExpBuffer copyBuf;
|
||||||
DataDumperPtr dumpFn;
|
DataDumperPtr dumpFn;
|
||||||
char *copyStmt;
|
char *copyStmt;
|
||||||
|
|
||||||
|
if (!tdinfo->dobj.dump)
|
||||||
|
return;
|
||||||
|
|
||||||
|
copyBuf = createPQExpBuffer();
|
||||||
|
|
||||||
if (!dump_inserts)
|
if (!dump_inserts)
|
||||||
{
|
{
|
||||||
/* Dump/restore using COPY */
|
/* Dump/restore using COPY */
|
||||||
@ -1539,32 +1568,42 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids)
|
|||||||
&& no_unlogged_table_data)
|
&& no_unlogged_table_data)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (tblinfo[i].dobj.dump)
|
if (tblinfo[i].dobj.dump && tblinfo[i].dataObj == NULL)
|
||||||
{
|
makeTableDataInfo(&(tblinfo[i]), oids);
|
||||||
TableDataInfo *tdinfo;
|
|
||||||
|
|
||||||
tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo));
|
|
||||||
|
|
||||||
tdinfo->dobj.objType = DO_TABLE_DATA;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: use tableoid 0 so that this object won't be mistaken for
|
|
||||||
* something that pg_depend entries apply to.
|
|
||||||
*/
|
|
||||||
tdinfo->dobj.catId.tableoid = 0;
|
|
||||||
tdinfo->dobj.catId.oid = tblinfo[i].dobj.catId.oid;
|
|
||||||
AssignDumpId(&tdinfo->dobj);
|
|
||||||
tdinfo->dobj.name = tblinfo[i].dobj.name;
|
|
||||||
tdinfo->dobj.namespace = tblinfo[i].dobj.namespace;
|
|
||||||
tdinfo->tdtable = &(tblinfo[i]);
|
|
||||||
tdinfo->oids = oids;
|
|
||||||
addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId);
|
|
||||||
|
|
||||||
tblinfo[i].dataObj = tdinfo;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a dumpable object for the data of this specific table
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
makeTableDataInfo(TableInfo *tbinfo, bool oids)
|
||||||
|
{
|
||||||
|
TableDataInfo *tdinfo;
|
||||||
|
|
||||||
|
tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo));
|
||||||
|
|
||||||
|
tdinfo->dobj.objType = DO_TABLE_DATA;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: use tableoid 0 so that this object won't be mistaken for
|
||||||
|
* something that pg_depend entries apply to.
|
||||||
|
*/
|
||||||
|
tdinfo->dobj.catId.tableoid = 0;
|
||||||
|
tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
|
||||||
|
AssignDumpId(&tdinfo->dobj);
|
||||||
|
tdinfo->dobj.name = tbinfo->dobj.name;
|
||||||
|
tdinfo->dobj.namespace = tbinfo->dobj.namespace;
|
||||||
|
tdinfo->dobj.dump = true;
|
||||||
|
tdinfo->tdtable = tbinfo;
|
||||||
|
tdinfo->oids = oids;
|
||||||
|
tdinfo->ext_config = false; /* might get set later */
|
||||||
|
tdinfo->filtercond = NULL; /* might get set later */
|
||||||
|
addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
|
||||||
|
|
||||||
|
tbinfo->dataObj = tdinfo;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* getTableDataFKConstraints -
|
* getTableDataFKConstraints -
|
||||||
* add dump-order dependencies reflecting foreign key constraints
|
* add dump-order dependencies reflecting foreign key constraints
|
||||||
@ -2584,6 +2623,123 @@ findNamespace(Oid nsoid, Oid objoid)
|
|||||||
return NULL; /* keep compiler quiet */
|
return NULL; /* keep compiler quiet */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getExtensions:
|
||||||
|
* read all extensions in the system catalogs and return them in the
|
||||||
|
* ExtensionInfo* structure
|
||||||
|
*
|
||||||
|
* numExtensions is set to the number of extensions read in
|
||||||
|
*/
|
||||||
|
ExtensionInfo *
|
||||||
|
getExtensions(int *numExtensions)
|
||||||
|
{
|
||||||
|
PGresult *res;
|
||||||
|
int ntups;
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
PQExpBuffer query;
|
||||||
|
ExtensionInfo *extinfo;
|
||||||
|
int i_tableoid;
|
||||||
|
int i_oid;
|
||||||
|
int i_extname;
|
||||||
|
int i_nspname;
|
||||||
|
int i_extconfig;
|
||||||
|
int i_extcondition;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Before 9.1, there are no extensions.
|
||||||
|
*/
|
||||||
|
if (g_fout->remoteVersion < 90100)
|
||||||
|
{
|
||||||
|
*numExtensions = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
query = createPQExpBuffer();
|
||||||
|
|
||||||
|
/* Make sure we are in proper schema */
|
||||||
|
selectSourceSchema("pg_catalog");
|
||||||
|
|
||||||
|
appendPQExpBuffer(query, "SELECT x.tableoid, x.oid, "
|
||||||
|
"x.extname, n.nspname, x.extconfig, x.extcondition "
|
||||||
|
"FROM pg_extension x "
|
||||||
|
"JOIN pg_namespace n ON n.oid = x.extnamespace");
|
||||||
|
|
||||||
|
res = PQexec(g_conn, query->data);
|
||||||
|
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
|
||||||
|
|
||||||
|
ntups = PQntuples(res);
|
||||||
|
|
||||||
|
extinfo = (ExtensionInfo *) malloc(ntups * sizeof(ExtensionInfo));
|
||||||
|
|
||||||
|
i_tableoid = PQfnumber(res, "tableoid");
|
||||||
|
i_oid = PQfnumber(res, "oid");
|
||||||
|
i_extname = PQfnumber(res, "extname");
|
||||||
|
i_nspname = PQfnumber(res, "nspname");
|
||||||
|
i_extconfig = PQfnumber(res, "extconfig");
|
||||||
|
i_extcondition = PQfnumber(res, "extcondition");
|
||||||
|
|
||||||
|
for (i = 0; i < ntups; i++)
|
||||||
|
{
|
||||||
|
char *extconfig;
|
||||||
|
char *extcondition;
|
||||||
|
char **extconfigarray = NULL;
|
||||||
|
char **extconditionarray = NULL;
|
||||||
|
int nconfigitems;
|
||||||
|
int nconditionitems;
|
||||||
|
|
||||||
|
extinfo[i].dobj.objType = DO_EXTENSION;
|
||||||
|
extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
|
||||||
|
extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
|
||||||
|
AssignDumpId(&extinfo[i].dobj);
|
||||||
|
extinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_extname));
|
||||||
|
extinfo[i].namespace = strdup(PQgetvalue(res, i, i_nspname));
|
||||||
|
|
||||||
|
/* For the moment, all extensions are considered dumpable */
|
||||||
|
extinfo->dobj.dump = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find and mark any configuration tables for this extension.
|
||||||
|
*
|
||||||
|
* Note that we create TableDataInfo objects even in schemaOnly mode,
|
||||||
|
* ie, user data in a configuration table is treated like schema data.
|
||||||
|
* This seems appropriate since system data in a config table would
|
||||||
|
* get reloaded by CREATE EXTENSION.
|
||||||
|
*/
|
||||||
|
extconfig = PQgetvalue(res, i, i_extconfig);
|
||||||
|
extcondition = PQgetvalue(res, i, i_extcondition);
|
||||||
|
if (parsePGArray(extconfig, &extconfigarray, &nconfigitems) &&
|
||||||
|
parsePGArray(extcondition, &extconditionarray, &nconditionitems) &&
|
||||||
|
nconfigitems == nconditionitems)
|
||||||
|
{
|
||||||
|
for (j = 0; j < nconfigitems; j++)
|
||||||
|
{
|
||||||
|
TableInfo *configtbl;
|
||||||
|
|
||||||
|
configtbl = findTableByOid(atooid(extconfigarray[j]));
|
||||||
|
if (configtbl && configtbl->dataObj == NULL)
|
||||||
|
{
|
||||||
|
makeTableDataInfo(configtbl, false);
|
||||||
|
configtbl->dataObj->ext_config = true;
|
||||||
|
if (strlen(extconditionarray[j]) > 0)
|
||||||
|
configtbl->dataObj->filtercond = strdup(extconditionarray[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (extconfigarray)
|
||||||
|
free(extconfigarray);
|
||||||
|
if (extconditionarray)
|
||||||
|
free(extconditionarray);
|
||||||
|
}
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
destroyPQExpBuffer(query);
|
||||||
|
|
||||||
|
*numExtensions = ntups;
|
||||||
|
|
||||||
|
return extinfo;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* getTypes:
|
* getTypes:
|
||||||
* read all types in the system catalogs and return them in the
|
* read all types in the system catalogs and return them in the
|
||||||
@ -5044,6 +5200,9 @@ getProcLangs(int *numProcLangs)
|
|||||||
else
|
else
|
||||||
planginfo[i].lanowner = strdup("");
|
planginfo[i].lanowner = strdup("");
|
||||||
|
|
||||||
|
/* Assume it should be dumped (getDependencies may override this) */
|
||||||
|
planginfo[i].dobj.dump = true;
|
||||||
|
|
||||||
if (g_fout->remoteVersion < 70300)
|
if (g_fout->remoteVersion < 70300)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -5151,6 +5310,9 @@ getCasts(int *numCasts)
|
|||||||
castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
|
castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
|
||||||
castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
|
castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
|
||||||
|
|
||||||
|
/* Assume it should be dumped (getDependencies may override this) */
|
||||||
|
castinfo[i].dobj.dump = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to name cast as concatenation of typnames. This is only used
|
* Try to name cast as concatenation of typnames. This is only used
|
||||||
* for purposes of sorting. If we fail to find either type, the name
|
* for purposes of sorting. If we fail to find either type, the name
|
||||||
@ -6585,6 +6747,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
|
|||||||
case DO_NAMESPACE:
|
case DO_NAMESPACE:
|
||||||
dumpNamespace(fout, (NamespaceInfo *) dobj);
|
dumpNamespace(fout, (NamespaceInfo *) dobj);
|
||||||
break;
|
break;
|
||||||
|
case DO_EXTENSION:
|
||||||
|
dumpExtension(fout, (ExtensionInfo *) dobj);
|
||||||
|
break;
|
||||||
case DO_TYPE:
|
case DO_TYPE:
|
||||||
dumpType(fout, (TypeInfo *) dobj);
|
dumpType(fout, (TypeInfo *) dobj);
|
||||||
break;
|
break;
|
||||||
@ -6734,6 +6899,56 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
|
|||||||
destroyPQExpBuffer(delq);
|
destroyPQExpBuffer(delq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dumpExtension
|
||||||
|
* writes out to fout the queries to recreate an extension
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
dumpExtension(Archive *fout, ExtensionInfo *extinfo)
|
||||||
|
{
|
||||||
|
PQExpBuffer q;
|
||||||
|
PQExpBuffer delq;
|
||||||
|
char *qextname;
|
||||||
|
|
||||||
|
/* Skip if not to be dumped */
|
||||||
|
if (!extinfo->dobj.dump || dataOnly)
|
||||||
|
return;
|
||||||
|
|
||||||
|
q = createPQExpBuffer();
|
||||||
|
delq = createPQExpBuffer();
|
||||||
|
|
||||||
|
qextname = strdup(fmtId(extinfo->dobj.name));
|
||||||
|
|
||||||
|
appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
|
||||||
|
|
||||||
|
appendPQExpBuffer(q, "CREATE EXTENSION %s WITH SCHEMA %s;\n",
|
||||||
|
qextname, fmtId(extinfo->namespace));
|
||||||
|
|
||||||
|
ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
|
||||||
|
extinfo->dobj.name,
|
||||||
|
NULL, NULL,
|
||||||
|
"",
|
||||||
|
false, "EXTENSION", SECTION_PRE_DATA,
|
||||||
|
q->data, delq->data, NULL,
|
||||||
|
extinfo->dobj.dependencies, extinfo->dobj.nDeps,
|
||||||
|
NULL, NULL);
|
||||||
|
|
||||||
|
/* Dump Extension Comments and Security Labels */
|
||||||
|
resetPQExpBuffer(q);
|
||||||
|
appendPQExpBuffer(q, "EXTENSION %s", qextname);
|
||||||
|
dumpComment(fout, q->data,
|
||||||
|
NULL, "",
|
||||||
|
extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
|
||||||
|
dumpSecLabel(fout, q->data,
|
||||||
|
NULL, "",
|
||||||
|
extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
|
||||||
|
|
||||||
|
free(qextname);
|
||||||
|
|
||||||
|
destroyPQExpBuffer(q);
|
||||||
|
destroyPQExpBuffer(delq);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dumpType
|
* dumpType
|
||||||
* writes out to fout the queries to recreate a user-defined type
|
* writes out to fout the queries to recreate a user-defined type
|
||||||
@ -7696,7 +7911,8 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
|
|||||||
FuncInfo *inlineInfo = NULL;
|
FuncInfo *inlineInfo = NULL;
|
||||||
FuncInfo *validatorInfo = NULL;
|
FuncInfo *validatorInfo = NULL;
|
||||||
|
|
||||||
if (dataOnly)
|
/* Skip if not to be dumped */
|
||||||
|
if (!plang->dobj.dump || dataOnly)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -8418,7 +8634,8 @@ dumpCast(Archive *fout, CastInfo *cast)
|
|||||||
TypeInfo *sourceInfo;
|
TypeInfo *sourceInfo;
|
||||||
TypeInfo *targetInfo;
|
TypeInfo *targetInfo;
|
||||||
|
|
||||||
if (dataOnly)
|
/* Skip if not to be dumped */
|
||||||
|
if (!cast->dobj.dump || dataOnly)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (OidIsValid(cast->castfunc))
|
if (OidIsValid(cast->castfunc))
|
||||||
@ -12747,6 +12964,24 @@ getDependencies(void)
|
|||||||
else
|
else
|
||||||
/* normal case */
|
/* normal case */
|
||||||
addObjectDependency(dobj, refdobj->dumpId);
|
addObjectDependency(dobj, refdobj->dumpId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's an extension-membership dependency, mark the member
|
||||||
|
* object as not to be dumped. We still need the dependency links,
|
||||||
|
* though, to ensure that sorting is done correctly.
|
||||||
|
*/
|
||||||
|
if (deptype == 'e')
|
||||||
|
{
|
||||||
|
dobj->dump = false;
|
||||||
|
if (dobj->objType == DO_TABLE)
|
||||||
|
{
|
||||||
|
/* Mark the data as not to be dumped either, unless config */
|
||||||
|
TableDataInfo *tdinfo = ((TableInfo *) dobj)->dataObj;
|
||||||
|
|
||||||
|
if (tdinfo && !tdinfo->ext_config)
|
||||||
|
tdinfo->dobj.dump = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
@ -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
|
|||||||
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
|
|||||||
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);
|
|||||||
* 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);
|
||||||
|
@ -22,13 +22,14 @@ static const char *modulename = gettext_noop("sorter");
|
|||||||
* Sort priority for object types when dumping a pre-7.3 database.
|
* Sort priority for object types when dumping a pre-7.3 database.
|
||||||
* Objects are sorted by priority levels, and within an equal priority level
|
* Objects are sorted by priority levels, and within an equal priority level
|
||||||
* by OID. (This is a relatively crude hack to provide semi-reasonable
|
* by OID. (This is a relatively crude hack to provide semi-reasonable
|
||||||
* behavior for old databases without full dependency info.) Note: text
|
* behavior for old databases without full dependency info.) Note: extensions,
|
||||||
* search, foreign-data, and default ACL objects can't really happen here,
|
* text search, foreign-data, and default ACL objects can't really happen here,
|
||||||
* so the rather bogus priorities for them don't matter.
|
* so the rather bogus priorities for them don't matter.
|
||||||
*/
|
*/
|
||||||
static const int oldObjectTypePriority[] =
|
static const int oldObjectTypePriority[] =
|
||||||
{
|
{
|
||||||
1, /* DO_NAMESPACE */
|
1, /* DO_NAMESPACE */
|
||||||
|
1, /* DO_EXTENSION */
|
||||||
2, /* DO_TYPE */
|
2, /* DO_TYPE */
|
||||||
2, /* DO_SHELL_TYPE */
|
2, /* DO_SHELL_TYPE */
|
||||||
2, /* DO_FUNC */
|
2, /* DO_FUNC */
|
||||||
@ -66,34 +67,35 @@ static const int oldObjectTypePriority[] =
|
|||||||
static const int newObjectTypePriority[] =
|
static const int newObjectTypePriority[] =
|
||||||
{
|
{
|
||||||
1, /* DO_NAMESPACE */
|
1, /* DO_NAMESPACE */
|
||||||
3, /* DO_TYPE */
|
3, /* DO_EXTENSION */
|
||||||
3, /* DO_SHELL_TYPE */
|
4, /* DO_TYPE */
|
||||||
4, /* DO_FUNC */
|
4, /* DO_SHELL_TYPE */
|
||||||
5, /* DO_AGG */
|
5, /* DO_FUNC */
|
||||||
6, /* DO_OPERATOR */
|
6, /* DO_AGG */
|
||||||
7, /* DO_OPCLASS */
|
7, /* DO_OPERATOR */
|
||||||
7, /* DO_OPFAMILY */
|
8, /* DO_OPCLASS */
|
||||||
9, /* DO_CONVERSION */
|
8, /* DO_OPFAMILY */
|
||||||
16, /* DO_TABLE */
|
10, /* DO_CONVERSION */
|
||||||
18, /* DO_ATTRDEF */
|
17, /* DO_TABLE */
|
||||||
23, /* DO_INDEX */
|
19, /* DO_ATTRDEF */
|
||||||
24, /* DO_RULE */
|
24, /* DO_INDEX */
|
||||||
25, /* DO_TRIGGER */
|
25, /* DO_RULE */
|
||||||
22, /* DO_CONSTRAINT */
|
26, /* DO_TRIGGER */
|
||||||
26, /* DO_FK_CONSTRAINT */
|
23, /* DO_CONSTRAINT */
|
||||||
|
27, /* DO_FK_CONSTRAINT */
|
||||||
2, /* DO_PROCLANG */
|
2, /* DO_PROCLANG */
|
||||||
8, /* DO_CAST */
|
9, /* DO_CAST */
|
||||||
20, /* DO_TABLE_DATA */
|
21, /* DO_TABLE_DATA */
|
||||||
17, /* DO_DUMMY_TYPE */
|
18, /* DO_DUMMY_TYPE */
|
||||||
10, /* DO_TSPARSER */
|
11, /* DO_TSPARSER */
|
||||||
12, /* DO_TSDICT */
|
13, /* DO_TSDICT */
|
||||||
11, /* DO_TSTEMPLATE */
|
12, /* DO_TSTEMPLATE */
|
||||||
13, /* DO_TSCONFIG */
|
14, /* DO_TSCONFIG */
|
||||||
14, /* DO_FDW */
|
15, /* DO_FDW */
|
||||||
15, /* DO_FOREIGN_SERVER */
|
16, /* DO_FOREIGN_SERVER */
|
||||||
27, /* DO_DEFAULT_ACL */
|
28, /* DO_DEFAULT_ACL */
|
||||||
19, /* DO_BLOB */
|
20, /* DO_BLOB */
|
||||||
21 /* DO_BLOB_DATA */
|
22 /* DO_BLOB_DATA */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1023,6 +1025,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
|
|||||||
"SCHEMA %s (ID %d OID %u)",
|
"SCHEMA %s (ID %d OID %u)",
|
||||||
obj->name, obj->dumpId, obj->catId.oid);
|
obj->name, obj->dumpId, obj->catId.oid);
|
||||||
return;
|
return;
|
||||||
|
case DO_EXTENSION:
|
||||||
|
snprintf(buf, bufsize,
|
||||||
|
"EXTENSION %s (ID %d OID %u)",
|
||||||
|
obj->name, obj->dumpId, obj->catId.oid);
|
||||||
|
return;
|
||||||
case DO_TYPE:
|
case DO_TYPE:
|
||||||
snprintf(buf, bufsize,
|
snprintf(buf, bufsize,
|
||||||
"TYPE %s (ID %d OID %u)",
|
"TYPE %s (ID %d OID %u)",
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ static bool describeOneTSConfig(const char *oid, const char *nspname,
|
|||||||
const char *cfgname,
|
const char *cfgname,
|
||||||
const char *pnspname, const char *prsname);
|
const char *pnspname, const char *prsname);
|
||||||
static void printACLColumn(PQExpBuffer buf, const char *colname);
|
static void printACLColumn(PQExpBuffer buf, const char *colname);
|
||||||
|
static bool listOneExtensionContents(const char *extname, const char *oid);
|
||||||
|
|
||||||
|
|
||||||
/*----------------
|
/*----------------
|
||||||
@ -3671,7 +3672,7 @@ listForeignTables(const char *pattern, bool verbose)
|
|||||||
|
|
||||||
if (pset.sversion < 90100)
|
if (pset.sversion < 90100)
|
||||||
{
|
{
|
||||||
fprintf(stderr, _("The server (version %d.%d) does not support foreign table.\n"),
|
fprintf(stderr, _("The server (version %d.%d) does not support foreign tables.\n"),
|
||||||
pset.sversion / 10000, (pset.sversion / 100) % 100);
|
pset.sversion / 10000, (pset.sversion / 100) % 100);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3718,6 +3719,167 @@ listForeignTables(const char *pattern, bool verbose)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* \dx
|
||||||
|
*
|
||||||
|
* Briefly describes installed extensions.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
listExtensions(const char *pattern)
|
||||||
|
{
|
||||||
|
PQExpBufferData buf;
|
||||||
|
PGresult *res;
|
||||||
|
printQueryOpt myopt = pset.popt;
|
||||||
|
|
||||||
|
if (pset.sversion < 90100)
|
||||||
|
{
|
||||||
|
fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"),
|
||||||
|
pset.sversion / 10000, (pset.sversion / 100) % 100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
initPQExpBuffer(&buf);
|
||||||
|
printfPQExpBuffer(&buf,
|
||||||
|
"SELECT e.extname AS \"%s\", "
|
||||||
|
"e.extversion AS \"%s\", n.nspname AS \"%s\", c.description AS \"%s\"\n"
|
||||||
|
"FROM pg_catalog.pg_extension e "
|
||||||
|
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
|
||||||
|
"LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid "
|
||||||
|
"AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass\n",
|
||||||
|
gettext_noop("Name"),
|
||||||
|
gettext_noop("Version"),
|
||||||
|
gettext_noop("Schema"),
|
||||||
|
gettext_noop("Description"));
|
||||||
|
|
||||||
|
processSQLNamePattern(pset.db, &buf, pattern,
|
||||||
|
false, false,
|
||||||
|
NULL, "e.extname", NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
appendPQExpBuffer(&buf, "ORDER BY 1;");
|
||||||
|
|
||||||
|
res = PSQLexec(buf.data, false);
|
||||||
|
termPQExpBuffer(&buf);
|
||||||
|
if (!res)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
myopt.nullPrint = NULL;
|
||||||
|
myopt.title = _("List of installed extensions");
|
||||||
|
myopt.translate_header = true;
|
||||||
|
|
||||||
|
printQuery(res, &myopt, pset.queryFout, pset.logfile);
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* \dx+
|
||||||
|
*
|
||||||
|
* List contents of installed extensions.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
listExtensionContents(const char *pattern)
|
||||||
|
{
|
||||||
|
PQExpBufferData buf;
|
||||||
|
PGresult *res;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (pset.sversion < 90100)
|
||||||
|
{
|
||||||
|
fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"),
|
||||||
|
pset.sversion / 10000, (pset.sversion / 100) % 100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
initPQExpBuffer(&buf);
|
||||||
|
printfPQExpBuffer(&buf,
|
||||||
|
"SELECT e.extname, e.oid\n"
|
||||||
|
"FROM pg_catalog.pg_extension e\n");
|
||||||
|
|
||||||
|
processSQLNamePattern(pset.db, &buf, pattern,
|
||||||
|
false, false,
|
||||||
|
NULL, "e.extname", NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
appendPQExpBuffer(&buf, "ORDER BY 1;");
|
||||||
|
|
||||||
|
res = PSQLexec(buf.data, false);
|
||||||
|
termPQExpBuffer(&buf);
|
||||||
|
if (!res)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (PQntuples(res) == 0)
|
||||||
|
{
|
||||||
|
if (!pset.quiet)
|
||||||
|
{
|
||||||
|
if (pattern)
|
||||||
|
fprintf(stderr, _("Did not find any extension named \"%s\".\n"),
|
||||||
|
pattern);
|
||||||
|
else
|
||||||
|
fprintf(stderr, _("Did not find any extensions.\n"));
|
||||||
|
}
|
||||||
|
PQclear(res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < PQntuples(res); i++)
|
||||||
|
{
|
||||||
|
const char *extname;
|
||||||
|
const char *oid;
|
||||||
|
|
||||||
|
extname = PQgetvalue(res, i, 0);
|
||||||
|
oid = PQgetvalue(res, i, 1);
|
||||||
|
|
||||||
|
if (!listOneExtensionContents(extname, oid))
|
||||||
|
{
|
||||||
|
PQclear(res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cancel_pressed)
|
||||||
|
{
|
||||||
|
PQclear(res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
listOneExtensionContents(const char *extname, const char *oid)
|
||||||
|
{
|
||||||
|
PQExpBufferData buf;
|
||||||
|
PGresult *res;
|
||||||
|
char title[1024];
|
||||||
|
printQueryOpt myopt = pset.popt;
|
||||||
|
|
||||||
|
initPQExpBuffer(&buf);
|
||||||
|
printfPQExpBuffer(&buf,
|
||||||
|
"SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
|
||||||
|
"FROM pg_catalog.pg_depend\n"
|
||||||
|
"WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
|
||||||
|
"ORDER BY 1;",
|
||||||
|
gettext_noop("Object Description"),
|
||||||
|
oid);
|
||||||
|
|
||||||
|
res = PSQLexec(buf.data, false);
|
||||||
|
termPQExpBuffer(&buf);
|
||||||
|
if (!res)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
myopt.nullPrint = NULL;
|
||||||
|
snprintf(title, sizeof(title), _("Objects in extension \"%s\""), extname);
|
||||||
|
myopt.title = title;
|
||||||
|
myopt.translate_header = true;
|
||||||
|
|
||||||
|
printQuery(res, &myopt, pset.queryFout, pset.logfile);
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* printACLColumn
|
* printACLColumn
|
||||||
*
|
*
|
||||||
|
@ -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)
|
|||||||
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"));
|
||||||
|
@ -578,6 +578,16 @@ static const SchemaQuery Query_for_list_of_views = {
|
|||||||
" FROM pg_catalog.pg_proc "\
|
" FROM pg_catalog.pg_proc "\
|
||||||
" WHERE proname='%s'"
|
" WHERE proname='%s'"
|
||||||
|
|
||||||
|
#define Query_for_list_of_extensions \
|
||||||
|
" SELECT pg_catalog.quote_ident(extname) "\
|
||||||
|
" FROM pg_catalog.pg_extension "\
|
||||||
|
" WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'"
|
||||||
|
|
||||||
|
#define Query_for_list_of_available_extensions \
|
||||||
|
" SELECT pg_catalog.quote_ident(name) "\
|
||||||
|
" FROM pg_catalog.pg_available_extensions "\
|
||||||
|
" WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed IS NULL"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a list of all "things" in Pgsql, which can show up after CREATE or
|
* This is a list of all "things" in Pgsql, which can show up after CREATE or
|
||||||
* DROP; and there is also a query to get a list of them.
|
* DROP; and there is also a query to get a list of them.
|
||||||
@ -606,6 +616,7 @@ static const pgsql_thing_t words_after_create[] = {
|
|||||||
{"DATABASE", Query_for_list_of_databases},
|
{"DATABASE", Query_for_list_of_databases},
|
||||||
{"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
|
{"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
|
||||||
{"DOMAIN", NULL, &Query_for_list_of_domains},
|
{"DOMAIN", NULL, &Query_for_list_of_domains},
|
||||||
|
{"EXTENSION", Query_for_list_of_extensions},
|
||||||
{"FOREIGN DATA WRAPPER", NULL, NULL},
|
{"FOREIGN DATA WRAPPER", NULL, NULL},
|
||||||
{"FOREIGN TABLE", NULL, NULL},
|
{"FOREIGN TABLE", NULL, NULL},
|
||||||
{"FUNCTION", NULL, &Query_for_list_of_functions},
|
{"FUNCTION", NULL, &Query_for_list_of_functions},
|
||||||
@ -775,9 +786,12 @@ psql_completion(char *text, int start, int end)
|
|||||||
pg_strcasecmp(prev3_wd, "TABLE") != 0)
|
pg_strcasecmp(prev3_wd, "TABLE") != 0)
|
||||||
{
|
{
|
||||||
static const char *const list_ALTER[] =
|
static const char *const list_ALTER[] =
|
||||||
{"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
|
{"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
|
||||||
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
|
"EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
|
||||||
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
|
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR",
|
||||||
|
"ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
|
||||||
|
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
|
||||||
|
"USER", "USER MAPPING FOR", "VIEW", NULL};
|
||||||
|
|
||||||
COMPLETE_WITH_LIST(list_ALTER);
|
COMPLETE_WITH_LIST(list_ALTER);
|
||||||
}
|
}
|
||||||
@ -838,6 +852,11 @@ psql_completion(char *text, int start, int end)
|
|||||||
COMPLETE_WITH_LIST(list_ALTERDATABASE);
|
COMPLETE_WITH_LIST(list_ALTERDATABASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ALTER EXTENSION <name> */
|
||||||
|
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
|
||||||
|
pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
|
||||||
|
COMPLETE_WITH_CONST("SET SCHEMA");
|
||||||
|
|
||||||
/* ALTER FOREIGN */
|
/* ALTER FOREIGN */
|
||||||
else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
|
else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
|
||||||
pg_strcasecmp(prev_wd, "FOREIGN") == 0)
|
pg_strcasecmp(prev_wd, "FOREIGN") == 0)
|
||||||
@ -1579,6 +1598,16 @@ psql_completion(char *text, int start, int end)
|
|||||||
pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
|
pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
|
||||||
COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
|
COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
|
||||||
|
|
||||||
|
/* CREATE EXTENSION */
|
||||||
|
/* Complete with available extensions rather than installed ones. */
|
||||||
|
else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
|
||||||
|
pg_strcasecmp(prev_wd, "EXTENSION") == 0)
|
||||||
|
COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
|
||||||
|
/* CREATE EXTENSION <name> */
|
||||||
|
else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
|
||||||
|
pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
|
||||||
|
COMPLETE_WITH_CONST("WITH SCHEMA");
|
||||||
|
|
||||||
/* CREATE FOREIGN */
|
/* CREATE FOREIGN */
|
||||||
else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
|
else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
|
||||||
pg_strcasecmp(prev_wd, "FOREIGN") == 0)
|
pg_strcasecmp(prev_wd, "FOREIGN") == 0)
|
||||||
@ -1922,6 +1951,7 @@ psql_completion(char *text, int start, int end)
|
|||||||
else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 &&
|
else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 &&
|
||||||
(pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
|
(pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
|
||||||
pg_strcasecmp(prev2_wd, "DOMAIN") == 0 ||
|
pg_strcasecmp(prev2_wd, "DOMAIN") == 0 ||
|
||||||
|
pg_strcasecmp(prev2_wd, "EXTENSION") == 0 ||
|
||||||
pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
|
pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
|
||||||
pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
|
pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
|
||||||
pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 ||
|
pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 ||
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201102083
|
#define CATALOG_VERSION_NO 201102084
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -50,6 +50,12 @@
|
|||||||
* Example: a trigger that's created to enforce a foreign-key constraint
|
* Example: a trigger that's created to enforce a foreign-key constraint
|
||||||
* is made internally dependent on the constraint's pg_constraint entry.
|
* is made internally dependent on the constraint's pg_constraint entry.
|
||||||
*
|
*
|
||||||
|
* DEPENDENCY_EXTENSION ('e'): the dependent object is a member of the
|
||||||
|
* extension that is the referenced object. The dependent object can be
|
||||||
|
* dropped only via 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 pg_dump.
|
||||||
|
*
|
||||||
* DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry
|
* DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry
|
||||||
* is a signal that the system itself depends on the referenced object,
|
* is a signal that the system itself depends on the referenced object,
|
||||||
* and so that object must never be deleted. Entries of this type are
|
* and so that object must never be deleted. Entries of this type are
|
||||||
@ -64,6 +70,7 @@ typedef enum DependencyType
|
|||||||
DEPENDENCY_NORMAL = 'n',
|
DEPENDENCY_NORMAL = 'n',
|
||||||
DEPENDENCY_AUTO = 'a',
|
DEPENDENCY_AUTO = 'a',
|
||||||
DEPENDENCY_INTERNAL = 'i',
|
DEPENDENCY_INTERNAL = 'i',
|
||||||
|
DEPENDENCY_EXTENSION = 'e',
|
||||||
DEPENDENCY_PIN = 'p'
|
DEPENDENCY_PIN = 'p'
|
||||||
} DependencyType;
|
} DependencyType;
|
||||||
|
|
||||||
@ -137,8 +144,8 @@ typedef enum ObjectClass
|
|||||||
OCLASS_FDW, /* pg_foreign_data_wrapper */
|
OCLASS_FDW, /* pg_foreign_data_wrapper */
|
||||||
OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
|
OCLASS_FOREIGN_SERVER, /* pg_foreign_server */
|
||||||
OCLASS_USER_MAPPING, /* pg_user_mapping */
|
OCLASS_USER_MAPPING, /* pg_user_mapping */
|
||||||
OCLASS_FOREIGN_TABLE, /* pg_foreign_table */
|
|
||||||
OCLASS_DEFACL, /* pg_default_acl */
|
OCLASS_DEFACL, /* pg_default_acl */
|
||||||
|
OCLASS_EXTENSION, /* pg_extension */
|
||||||
MAX_OCLASS /* MUST BE LAST */
|
MAX_OCLASS /* MUST BE LAST */
|
||||||
} ObjectClass;
|
} ObjectClass;
|
||||||
|
|
||||||
@ -193,12 +200,17 @@ extern void recordMultipleDependencies(const ObjectAddress *depender,
|
|||||||
int nreferenced,
|
int nreferenced,
|
||||||
DependencyType behavior);
|
DependencyType behavior);
|
||||||
|
|
||||||
extern long deleteDependencyRecordsFor(Oid classId, Oid objectId);
|
extern void recordDependencyOnCurrentExtension(const ObjectAddress *object);
|
||||||
|
|
||||||
|
extern long deleteDependencyRecordsFor(Oid classId, Oid objectId,
|
||||||
|
bool skipExtensionDeps);
|
||||||
|
|
||||||
extern long changeDependencyFor(Oid classId, Oid objectId,
|
extern long changeDependencyFor(Oid classId, Oid objectId,
|
||||||
Oid refClassId, Oid oldRefObjectId,
|
Oid refClassId, Oid oldRefObjectId,
|
||||||
Oid newRefObjectId);
|
Oid newRefObjectId);
|
||||||
|
|
||||||
|
extern Oid getExtensionOfObject(Oid classId, Oid objectId);
|
||||||
|
|
||||||
extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId);
|
extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId);
|
||||||
|
|
||||||
extern void markSequenceUnowned(Oid seqId);
|
extern void markSequenceUnowned(Oid seqId);
|
||||||
|
@ -294,6 +294,12 @@ DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_rol
|
|||||||
DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
|
DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
|
||||||
#define SecLabelObjectIndexId 3597
|
#define SecLabelObjectIndexId 3597
|
||||||
|
|
||||||
|
DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(oid oid_ops));
|
||||||
|
#define ExtensionOidIndexId 3080
|
||||||
|
|
||||||
|
DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops));
|
||||||
|
#define ExtensionNameIndexId 3081
|
||||||
|
|
||||||
/* last step of initialization script: build the indexes declared above */
|
/* last step of initialization script: build the indexes declared above */
|
||||||
BUILD_INDICES
|
BUILD_INDICES
|
||||||
|
|
||||||
|
72
src/include/catalog/pg_extension.h
Normal file
72
src/include/catalog/pg_extension.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* pg_extension.h
|
||||||
|
* definition of the system "extension" relation (pg_extension)
|
||||||
|
* along with the relation's initial contents.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* src/include/catalog/pg_extension.h
|
||||||
|
*
|
||||||
|
* NOTES
|
||||||
|
* the genbki.pl script reads this file and generates .bki
|
||||||
|
* information from the DATA() statements.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef PG_EXTENSION_H
|
||||||
|
#define PG_EXTENSION_H
|
||||||
|
|
||||||
|
#include "catalog/genbki.h"
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* pg_extension definition. cpp turns this into
|
||||||
|
* typedef struct FormData_pg_extension
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
#define ExtensionRelationId 3079
|
||||||
|
|
||||||
|
CATALOG(pg_extension,3079)
|
||||||
|
{
|
||||||
|
NameData extname; /* extension name */
|
||||||
|
Oid extowner; /* extension owner */
|
||||||
|
Oid extnamespace; /* namespace of contained objects */
|
||||||
|
bool extrelocatable; /* if true, allow ALTER EXTENSION SET SCHEMA */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
|
||||||
|
*/
|
||||||
|
text extversion; /* extension version ID, if any */
|
||||||
|
Oid extconfig[1]; /* dumpable configuration tables */
|
||||||
|
text extcondition[1]; /* WHERE clauses for config tables */
|
||||||
|
} FormData_pg_extension;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* Form_pg_extension corresponds to a pointer to a tuple with
|
||||||
|
* the format of pg_extension relation.
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
typedef FormData_pg_extension *Form_pg_extension;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* compiler constants for pg_extension
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define Natts_pg_extension 7
|
||||||
|
#define Anum_pg_extension_extname 1
|
||||||
|
#define Anum_pg_extension_extowner 2
|
||||||
|
#define Anum_pg_extension_extnamespace 3
|
||||||
|
#define Anum_pg_extension_extrelocatable 4
|
||||||
|
#define Anum_pg_extension_extversion 5
|
||||||
|
#define Anum_pg_extension_extconfig 6
|
||||||
|
#define Anum_pg_extension_extcondition 7
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* pg_extension has no initial contents
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif /* PG_EXTENSION_H */
|
@ -4873,6 +4873,12 @@ DESCR("record greater than or equal");
|
|||||||
DATA(insert OID = 2987 ( btrecordcmp PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "2249 2249" _null_ _null_ _null_ _null_ btrecordcmp _null_ _null_ _null_ ));
|
DATA(insert OID = 2987 ( btrecordcmp PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "2249 2249" _null_ _null_ _null_ _null_ btrecordcmp _null_ _null_ _null_ ));
|
||||||
DESCR("btree less-equal-greater");
|
DESCR("btree less-equal-greater");
|
||||||
|
|
||||||
|
/* Extensions */
|
||||||
|
DATA(insert OID = 3082 ( pg_available_extensions PGNSP PGUID 12 10 100 0 f f f t t s 0 0 2249 "" "{19,25,16,25}" "{o,o,o,o}" "{name,version,relocatable,comment}" _null_ pg_available_extensions _null_ _null_ _null_ ));
|
||||||
|
DESCR("list available extensions");
|
||||||
|
DATA(insert OID = 3083 ( pg_extension_config_dump PGNSP PGUID 12 1 0 0 f f f t f v 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ pg_extension_config_dump _null_ _null_ _null_ ));
|
||||||
|
DESCR("flag an extension's table contents to be emitted by pg_dump");
|
||||||
|
|
||||||
/* SQL-spec window functions */
|
/* SQL-spec window functions */
|
||||||
DATA(insert OID = 3100 ( row_number PGNSP PGUID 12 1 0 0 f t f f f i 0 0 20 "" _null_ _null_ _null_ _null_ window_row_number _null_ _null_ _null_ ));
|
DATA(insert OID = 3100 ( row_number PGNSP PGUID 12 1 0 0 f t f f f i 0 0 20 "" _null_ _null_ _null_ _null_ window_row_number _null_ _null_ _null_ ));
|
||||||
DESCR("row number within partition");
|
DESCR("row number within partition");
|
||||||
|
@ -20,11 +20,11 @@
|
|||||||
|
|
||||||
extern void ExecRenameStmt(RenameStmt *stmt);
|
extern void ExecRenameStmt(RenameStmt *stmt);
|
||||||
extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
|
extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
|
||||||
extern void AlterObjectNamespace(Relation rel, int cacheId,
|
extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid);
|
||||||
Oid classId, Oid objid, Oid nspId,
|
extern Oid AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
|
||||||
int Anum_name, int Anum_namespace, int Anum_owner,
|
Oid objid, Oid nspOid,
|
||||||
AclObjectKind acl_kind,
|
int Anum_name, int Anum_namespace, int Anum_owner,
|
||||||
bool superuser_only);
|
AclObjectKind acl_kind);
|
||||||
extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt);
|
extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt);
|
||||||
|
|
||||||
#endif /* ALTER_H */
|
#endif /* ALTER_H */
|
||||||
|
@ -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 */
|
||||||
|
@ -66,6 +66,7 @@ extern void DropCast(DropCastStmt *stmt);
|
|||||||
extern void DropCastById(Oid castOid);
|
extern void DropCastById(Oid castOid);
|
||||||
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
|
extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
|
||||||
const char *newschema);
|
const char *newschema);
|
||||||
|
extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid);
|
||||||
extern void ExecuteDoStmt(DoStmt *stmt);
|
extern void ExecuteDoStmt(DoStmt *stmt);
|
||||||
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
|
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
|
||||||
|
|
||||||
@ -77,6 +78,7 @@ extern void AlterOperatorOwner(List *name, TypeName *typeName1,
|
|||||||
TypeName *typename2, Oid newOwnerId);
|
TypeName *typename2, Oid newOwnerId);
|
||||||
extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
|
extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
|
||||||
extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
|
extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
|
||||||
|
extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
|
||||||
|
|
||||||
/* commands/aggregatecmds.c */
|
/* commands/aggregatecmds.c */
|
||||||
extern void DefineAggregate(List *name, List *args, bool oldstyle,
|
extern void DefineAggregate(List *name, List *args, bool oldstyle,
|
||||||
@ -100,9 +102,11 @@ extern void RenameOpFamily(List *name, const char *access_method, const char *ne
|
|||||||
extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId);
|
extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId);
|
||||||
extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId);
|
extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId);
|
||||||
extern void AlterOpClassNamespace(List *name, char *access_method, const char *newschema);
|
extern void AlterOpClassNamespace(List *name, char *access_method, const char *newschema);
|
||||||
|
extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid);
|
||||||
extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId);
|
extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId);
|
||||||
extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId);
|
extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId);
|
||||||
extern void AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema);
|
extern void AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema);
|
||||||
|
extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid);
|
||||||
extern Oid get_am_oid(const char *amname, bool missing_ok);
|
extern Oid get_am_oid(const char *amname, bool missing_ok);
|
||||||
extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
|
extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
|
||||||
extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
|
extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
|
||||||
@ -111,6 +115,7 @@ extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
|
|||||||
extern void DefineTSParser(List *names, List *parameters);
|
extern void DefineTSParser(List *names, List *parameters);
|
||||||
extern void RenameTSParser(List *oldname, const char *newname);
|
extern void RenameTSParser(List *oldname, const char *newname);
|
||||||
extern void AlterTSParserNamespace(List *name, const char *newschema);
|
extern void AlterTSParserNamespace(List *name, const char *newschema);
|
||||||
|
extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
|
||||||
extern void RemoveTSParsers(DropStmt *drop);
|
extern void RemoveTSParsers(DropStmt *drop);
|
||||||
extern void RemoveTSParserById(Oid prsId);
|
extern void RemoveTSParserById(Oid prsId);
|
||||||
|
|
||||||
@ -121,10 +126,12 @@ extern void RemoveTSDictionaryById(Oid dictId);
|
|||||||
extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
|
extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
|
||||||
extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
|
extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
|
||||||
extern void AlterTSDictionaryNamespace(List *name, const char *newschema);
|
extern void AlterTSDictionaryNamespace(List *name, const char *newschema);
|
||||||
|
extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid);
|
||||||
|
|
||||||
extern void DefineTSTemplate(List *names, List *parameters);
|
extern void DefineTSTemplate(List *names, List *parameters);
|
||||||
extern void RenameTSTemplate(List *oldname, const char *newname);
|
extern void RenameTSTemplate(List *oldname, const char *newname);
|
||||||
extern void AlterTSTemplateNamespace(List *name, const char *newschema);
|
extern void AlterTSTemplateNamespace(List *name, const char *newschema);
|
||||||
|
extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
|
||||||
extern void RemoveTSTemplates(DropStmt *stmt);
|
extern void RemoveTSTemplates(DropStmt *stmt);
|
||||||
extern void RemoveTSTemplateById(Oid tmplId);
|
extern void RemoveTSTemplateById(Oid tmplId);
|
||||||
|
|
||||||
@ -135,6 +142,7 @@ extern void RemoveTSConfigurationById(Oid cfgId);
|
|||||||
extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
|
extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
|
||||||
extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
|
extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
|
||||||
extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
|
extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
|
||||||
|
extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
|
||||||
|
|
||||||
extern text *serialize_deflist(List *deflist);
|
extern text *serialize_deflist(List *deflist);
|
||||||
extern List *deserialize_deflist(Datum txt);
|
extern List *deserialize_deflist(Datum txt);
|
||||||
|
40
src/include/commands/extension.h
Normal file
40
src/include/commands/extension.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* extension.h
|
||||||
|
* Extension management commands (create/drop extension).
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* src/include/commands/extension.h
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef EXTENSION_H
|
||||||
|
#define EXTENSION_H
|
||||||
|
|
||||||
|
#include "nodes/parsenodes.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* creating_extension is only true while running a CREATE EXTENSION command.
|
||||||
|
* It instructs recordDependencyOnCurrentExtension() to register a dependency
|
||||||
|
* on the current pg_extension object for each SQL object created by its
|
||||||
|
* installation script.
|
||||||
|
*/
|
||||||
|
extern bool creating_extension;
|
||||||
|
extern Oid CurrentExtensionObject;
|
||||||
|
|
||||||
|
|
||||||
|
extern void CreateExtension(CreateExtensionStmt *stmt);
|
||||||
|
|
||||||
|
extern void RemoveExtensions(DropStmt *stmt);
|
||||||
|
extern void RemoveExtensionById(Oid extId);
|
||||||
|
|
||||||
|
extern Oid get_extension_oid(const char *extname, bool missing_ok);
|
||||||
|
extern char *get_extension_name(Oid ext_oid);
|
||||||
|
|
||||||
|
extern void AlterExtensionNamespace(List *names, const char *newschema);
|
||||||
|
|
||||||
|
#endif /* EXTENSION_H */
|
@ -41,7 +41,8 @@ extern void AlterTypeOwner(List *names, Oid newOwnerId);
|
|||||||
extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
|
extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
|
||||||
bool hasDependEntry);
|
bool hasDependEntry);
|
||||||
extern void AlterTypeNamespace(List *names, const char *newschema);
|
extern void AlterTypeNamespace(List *names, const char *newschema);
|
||||||
extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
|
extern Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid);
|
||||||
|
extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
|
||||||
bool isImplicitArray,
|
bool isImplicitArray,
|
||||||
bool errorOnTableType);
|
bool errorOnTableType);
|
||||||
|
|
||||||
|
@ -355,6 +355,7 @@ typedef enum NodeTag
|
|||||||
T_AlterTableSpaceOptionsStmt,
|
T_AlterTableSpaceOptionsStmt,
|
||||||
T_SecLabelStmt,
|
T_SecLabelStmt,
|
||||||
T_CreateForeignTableStmt,
|
T_CreateForeignTableStmt,
|
||||||
|
T_CreateExtensionStmt,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TAGS FOR PARSE TREE NODES (parsenodes.h)
|
* TAGS FOR PARSE TREE NODES (parsenodes.h)
|
||||||
|
@ -1073,6 +1073,7 @@ typedef enum ObjectType
|
|||||||
OBJECT_CONVERSION,
|
OBJECT_CONVERSION,
|
||||||
OBJECT_DATABASE,
|
OBJECT_DATABASE,
|
||||||
OBJECT_DOMAIN,
|
OBJECT_DOMAIN,
|
||||||
|
OBJECT_EXTENSION,
|
||||||
OBJECT_FDW,
|
OBJECT_FDW,
|
||||||
OBJECT_FOREIGN_SERVER,
|
OBJECT_FOREIGN_SERVER,
|
||||||
OBJECT_FOREIGN_TABLE,
|
OBJECT_FOREIGN_TABLE,
|
||||||
@ -1533,6 +1534,18 @@ typedef struct AlterTableSpaceOptionsStmt
|
|||||||
bool isReset;
|
bool isReset;
|
||||||
} AlterTableSpaceOptionsStmt;
|
} AlterTableSpaceOptionsStmt;
|
||||||
|
|
||||||
|
/* ----------------------
|
||||||
|
* Create Extension Statement
|
||||||
|
* ----------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct CreateExtensionStmt
|
||||||
|
{
|
||||||
|
NodeTag type;
|
||||||
|
char *extname;
|
||||||
|
List *options; /* List of DefElem nodes */
|
||||||
|
} CreateExtensionStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
* Create/Drop FOREIGN DATA WRAPPER Statements
|
* Create/Drop FOREIGN DATA WRAPPER Statements
|
||||||
* ----------------------
|
* ----------------------
|
||||||
|
@ -150,6 +150,7 @@ PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD)
|
|||||||
PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD)
|
PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD)
|
PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD)
|
||||||
PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD)
|
PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD)
|
||||||
|
PG_KEYWORD("extension", EXTENSION, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD)
|
PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD)
|
PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD)
|
||||||
PG_KEYWORD("false", FALSE_P, RESERVED_KEYWORD)
|
PG_KEYWORD("false", FALSE_P, RESERVED_KEYWORD)
|
||||||
|
@ -440,6 +440,8 @@ extern Datum pg_relation_filenode(PG_FUNCTION_ARGS);
|
|||||||
extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
|
extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
/* genfile.c */
|
/* genfile.c */
|
||||||
|
extern bytea *read_binary_file(const char *filename,
|
||||||
|
int64 seek_offset, int64 bytes_to_read);
|
||||||
extern Datum pg_stat_file(PG_FUNCTION_ARGS);
|
extern Datum pg_stat_file(PG_FUNCTION_ARGS);
|
||||||
extern Datum pg_read_file(PG_FUNCTION_ARGS);
|
extern Datum pg_read_file(PG_FUNCTION_ARGS);
|
||||||
extern Datum pg_read_file_all(PG_FUNCTION_ARGS);
|
extern Datum pg_read_file_all(PG_FUNCTION_ARGS);
|
||||||
@ -1063,6 +1065,10 @@ extern Datum pg_describe_object(PG_FUNCTION_ARGS);
|
|||||||
/* commands/constraint.c */
|
/* commands/constraint.c */
|
||||||
extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
|
extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* commands/extension.c */
|
||||||
|
extern Datum pg_available_extensions(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum pg_extension_config_dump(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
/* commands/prepare.c */
|
/* commands/prepare.c */
|
||||||
extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
|
extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
@ -17,16 +17,17 @@
|
|||||||
#
|
#
|
||||||
# Set one of these three variables to specify what is built:
|
# Set one of these three variables to specify what is built:
|
||||||
#
|
#
|
||||||
# MODULES -- list of shared objects to be built from source files with
|
# MODULES -- list of shared-library objects to be built from source files
|
||||||
# same stem (do not include suffix in this list)
|
# with same stem (do not include library suffixes in this list)
|
||||||
# MODULE_big -- a shared object to build from multiple source files
|
# MODULE_big -- a shared library to build from multiple source files
|
||||||
# (list object files in OBJS)
|
# (list object files in OBJS)
|
||||||
# PROGRAM -- a binary program to build (list object files in OBJS)
|
# PROGRAM -- an executable program to build (list object files in OBJS)
|
||||||
#
|
#
|
||||||
# The following variables can also be set:
|
# The following variables can also be set:
|
||||||
#
|
#
|
||||||
# MODULEDIR -- subdirectory into which DATA and DOCS files should be
|
# MODULEDIR -- subdirectory into which EXTENSION, DATA and DOCS files
|
||||||
# installed (if not set, default is "contrib")
|
# should be installed (if not set, default is "contrib")
|
||||||
|
# EXTENSION -- name of extension (there must be a $EXTENSION.control file)
|
||||||
# DATA -- random files to install into $PREFIX/share/$MODULEDIR
|
# DATA -- random files to install into $PREFIX/share/$MODULEDIR
|
||||||
# DATA_built -- random files to install into $PREFIX/share/$MODULEDIR,
|
# DATA_built -- random files to install into $PREFIX/share/$MODULEDIR,
|
||||||
# which need to be built first
|
# which need to be built first
|
||||||
@ -82,7 +83,7 @@ ifdef PG_CPPFLAGS
|
|||||||
override CPPFLAGS := $(PG_CPPFLAGS) $(CPPFLAGS)
|
override CPPFLAGS := $(PG_CPPFLAGS) $(CPPFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: $(PROGRAM) $(DATA_built) $(SCRIPTS_built) $(addsuffix $(DLSUFFIX), $(MODULES))
|
all: $(PROGRAM) $(DATA_built) $(SCRIPTS_built) $(addsuffix $(DLSUFFIX), $(MODULES)) $(addsuffix .control, $(EXTENSION))
|
||||||
|
|
||||||
ifdef MODULE_big
|
ifdef MODULE_big
|
||||||
# shared library parameters
|
# shared library parameters
|
||||||
@ -95,8 +96,8 @@ endif # MODULE_big
|
|||||||
|
|
||||||
|
|
||||||
install: all installdirs
|
install: all installdirs
|
||||||
ifneq (,$(DATA)$(DATA_built))
|
ifneq (,$(DATA)$(DATA_built)$(EXTENSION))
|
||||||
@for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \
|
@for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built) $(addsuffix .control, $(EXTENSION)); do \
|
||||||
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
|
echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
|
||||||
$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
|
$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
|
||||||
done
|
done
|
||||||
@ -167,8 +168,8 @@ endif # MODULE_big
|
|||||||
|
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
ifneq (,$(DATA)$(DATA_built))
|
ifneq (,$(DATA)$(DATA_built)$(EXTENSION))
|
||||||
rm -f $(addprefix '$(DESTDIR)$(datadir)/$(datamoduledir)'/, $(notdir $(DATA) $(DATA_built)))
|
rm -f $(addprefix '$(DESTDIR)$(datadir)/$(datamoduledir)'/, $(notdir $(DATA) $(DATA_built) $(addsuffix .control, $(EXTENSION))))
|
||||||
endif
|
endif
|
||||||
ifneq (,$(DATA_TSEARCH))
|
ifneq (,$(DATA_TSEARCH))
|
||||||
rm -f $(addprefix '$(DESTDIR)$(datadir)/tsearch_data'/, $(notdir $(DATA_TSEARCH)))
|
rm -f $(addprefix '$(DESTDIR)$(datadir)/tsearch_data'/, $(notdir $(DATA_TSEARCH)))
|
||||||
|
@ -1279,6 +1279,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
|
|||||||
viewname | definition
|
viewname | definition
|
||||||
-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
|
iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
|
||||||
|
pg_available_extensions | SELECT e.name, e.version, x.extversion AS installed, n.nspname AS schema, e.relocatable, e.comment FROM ((pg_available_extensions() e(name, version, relocatable, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))) LEFT JOIN pg_namespace n ON ((n.oid = x.extnamespace)));
|
||||||
pg_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
|
pg_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
|
||||||
pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
|
pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
|
||||||
pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS tablespace, pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char"));
|
pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS tablespace, pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char"));
|
||||||
@ -1336,7 +1337,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
|
|||||||
shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
|
shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
|
||||||
street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
|
street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
|
||||||
toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
|
toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
|
||||||
(58 rows)
|
(59 rows)
|
||||||
|
|
||||||
SELECT tablename, rulename, definition FROM pg_rules
|
SELECT tablename, rulename, definition FROM pg_rules
|
||||||
ORDER BY tablename, rulename;
|
ORDER BY tablename, rulename;
|
||||||
|
@ -101,6 +101,7 @@ SELECT relname, relhasindex
|
|||||||
pg_depend | t
|
pg_depend | t
|
||||||
pg_description | t
|
pg_description | t
|
||||||
pg_enum | t
|
pg_enum | t
|
||||||
|
pg_extension | t
|
||||||
pg_foreign_data_wrapper | t
|
pg_foreign_data_wrapper | t
|
||||||
pg_foreign_server | t
|
pg_foreign_server | t
|
||||||
pg_foreign_table | t
|
pg_foreign_table | t
|
||||||
|
Loading…
x
Reference in New Issue
Block a user