]> granicus.if.org Git - postgresql/commitdiff
Core support for "extensions", which are packages of SQL objects.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 8 Feb 2011 21:08:41 +0000 (16:08 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 8 Feb 2011 21:13:22 +0000 (16:13 -0500)
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

69 files changed:
doc/src/sgml/acronyms.sgml
doc/src/sgml/catalogs.sgml
doc/src/sgml/extend.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/alter_extension.sgml [new file with mode: 0644]
doc/src/sgml/ref/comment.sgml
doc/src/sgml/ref/create_extension.sgml [new file with mode: 0644]
doc/src/sgml/ref/drop_extension.sgml [new file with mode: 0644]
doc/src/sgml/ref/psql-ref.sgml
doc/src/sgml/reference.sgml
doc/src/sgml/release-9.0.sgml
doc/src/sgml/xfunc.sgml
src/backend/catalog/Makefile
src/backend/catalog/dependency.c
src/backend/catalog/heap.c
src/backend/catalog/objectaddress.c
src/backend/catalog/pg_conversion.c
src/backend/catalog/pg_depend.c
src/backend/catalog/pg_namespace.c
src/backend/catalog/pg_operator.c
src/backend/catalog/pg_proc.c
src/backend/catalog/pg_type.c
src/backend/catalog/system_views.sql
src/backend/commands/Makefile
src/backend/commands/alter.c
src/backend/commands/cluster.c
src/backend/commands/comment.c
src/backend/commands/conversioncmds.c
src/backend/commands/extension.c [new file with mode: 0644]
src/backend/commands/foreigncmds.c
src/backend/commands/functioncmds.c
src/backend/commands/opclasscmds.c
src/backend/commands/operatorcmds.c
src/backend/commands/proclang.c
src/backend/commands/tablecmds.c
src/backend/commands/tsearchcmds.c
src/backend/commands/typecmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/rewrite/rewriteDefine.c
src/backend/tcop/utility.c
src/backend/utils/adt/genfile.c
src/bin/pg_dump/common.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dump_sort.c
src/bin/psql/command.c
src/bin/psql/describe.c
src/bin/psql/describe.h
src/bin/psql/help.c
src/bin/psql/tab-complete.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/catalog/indexing.h
src/include/catalog/pg_extension.h [new file with mode: 0644]
src/include/catalog/pg_proc.h
src/include/commands/alter.h
src/include/commands/conversioncmds.h
src/include/commands/defrem.h
src/include/commands/extension.h [new file with mode: 0644]
src/include/commands/typecmds.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/kwlist.h
src/include/utils/builtins.h
src/makefiles/pgxs.mk
src/test/regress/expected/rules.out
src/test/regress/expected/sanity_check.out

index d1ef489e3666f39b03fcd46d8be5d1572b579444..8f6752f05d70f1c74b830600d4fe57e18316d3c8 100644 (file)
     <term><acronym>PGXS</acronym></term>
     <listitem>
      <para>
-      <link linkend="xfunc-c-pgxs"><productname>PostgreSQL</> Extension System</link>
+      <link linkend="extend-pgxs"><productname>PostgreSQL</> Extension System</link>
      </para>
     </listitem>
    </varlistentry>
index f31662c2720f7b6ef10c3b4a46a077d53d7b98e1..24aa22cbced804c05df94a65aada91c6e69f4bf2 100644 (file)
       <entry>enum label and value definitions</entry>
      </row>
 
+     <row>
+      <entry><link linkend="catalog-pg-extension"><structname>pg_extension</structname></link></entry>
+      <entry>installed extensions</entry>
+     </row>
+
      <row>
       <entry><link linkend="catalog-pg-foreign-data-wrapper"><structname>pg_foreign_data_wrapper</structname></link></entry>
       <entry>foreign-data wrapper definitions</entry>
      </listitem>
     </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>
      <term><symbol>DEPENDENCY_PIN</> (<literal>p</>)</term>
      <listitem>
  </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">
   <title><structname>pg_foreign_data_wrapper</structname></title>
 
     </thead>
 
     <tbody>
+     <row>
+      <entry><link linkend="view-pg-available-extensions"><structname>pg_available_extensions</structname></link></entry>
+      <entry>available extensions</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-cursors"><structname>pg_cursors</structname></link></entry>
       <entry>open cursors</entry>
   </table>
  </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">
   <title><structname>pg_cursors</structname></title>
 
index 2033ae21ba5261f91b48563c0e8ec16fd4db4ff0..206eb6b9017cb7f8205b1ff4c6def320fab94712 100644 (file)
       operator classes for indexes (starting in <xref linkend="xindex">)
      </para>
     </listitem>
+    <listitem>
+     <para>
+      packages of related objects (starting in <xref linkend="extend-extensions">)
+     </para>
+    </listitem>
    </itemizedlist>
   </para>
 
   &xoper;
   &xindex;
 
-  <sect1 id="extend-Cpp">
-   <title>Using C++ for Extensibility</title>
 
-   <indexterm zone="extend-Cpp">
-    <primary>C++</primary>
+  <sect1 id="extend-extensions">
+   <title>Packaging Related Objects into an Extension</title>
+
+   <indexterm zone="extend-extensions">
+    <primary>extension</primary>
    </indexterm>
 
    <para>
-    It is possible to use a compiler in C++ mode to build
-    <productname>PostgreSQL</productname> extensions by following these
-    guidelines:
+    A useful extension to <productname>PostgreSQL</> typically includes
+    multiple SQL objects; for example, a new datatype will require new
+    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 &mdash; 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>
      <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, i.e. using C++
-       <function>delete()</> in such cases will fail.
+       A fully relocatable extension can be moved into another schema
+       at any time, even after it's been loaded into a database.
+       This is done with the <command>ALTER EXTENSION SET SCHEMA</>
+       command, which automatically renames all the member objects into
+       the new schema.  Normally, this is only possible if the extension
+       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>
      </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
-       throw any exceptions because events like out-of-memory 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()</>.
+       An extension might be relocatable during installation but not
+       afterwards.  This is typically the case if the extension's script
+       file needs to reference the target schema explicitly, for example
+       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>
      </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.
+       If the extension does not support relocation at all, set
+       <literal>relocatable = false</> in its control file, and also set
+       <literal>schema</> to the name of the intended target schema.  This
+       will prevent use of the <literal>SCHEMA</> option of <command>CREATE
+       EXTENSION</>, unless it specifies the same schema named in the control
+       file.  This choice is typically necessary if the extension contains
+       internal assumptions about schema names that can't be replaced by
+       uses of <literal>@extschema@</>.  The <literal>@extschema@</>
+       substitution mechanism is available in this case too, although it is
+       of limited use since the schema name is determined by the control file.
       </para>
      </listitem>
     </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>
-    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.
+    To use the <acronym>PGXS</acronym> infrastructure for your extension,
+    you must write a simple makefile.
+    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>
+    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>
 
  </chapter>
index c44d11ef91b916523502454dfc18b20d92f1aab8..ba85cae0837e5c6246ac6ad832c42c8187a39eb7 100644 (file)
@@ -11,6 +11,7 @@ Complete list of usable sgml source files in this directory.
 <!entity alterDatabase      system "alter_database.sgml">
 <!entity alterDefaultPrivileges system "alter_default_privileges.sgml">
 <!entity alterDomain        system "alter_domain.sgml">
+<!entity alterExtension     system "alter_extension.sgml">
 <!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml">
 <!entity alterForeignTable  system "alter_foreign_table.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 createDatabase     system "create_database.sgml">
 <!entity createDomain       system "create_domain.sgml">
+<!entity createExtension    system "create_extension.sgml">
 <!entity createForeignDataWrapper system "create_foreign_data_wrapper.sgml">
 <!entity createForeignTable system "create_foreign_table.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 dropDatabase       system "drop_database.sgml">
 <!entity dropDomain         system "drop_domain.sgml">
+<!entity dropExtension      system "drop_extension.sgml">
 <!entity dropForeignDataWrapper system "drop_foreign_data_wrapper.sgml">
 <!entity dropForeignTable   system "drop_foreign_table.sgml">
 <!entity dropFunction       system "drop_function.sgml">
diff --git a/doc/src/sgml/ref/alter_extension.sgml b/doc/src/sgml/ref/alter_extension.sgml
new file mode 100644 (file)
index 0000000..1b29d27
--- /dev/null
@@ -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>
index f1a1605df3c48a9039fa94970998de63a0880e77..e1fe0c16f9a2c0bcf23cdc799376e0d934b60950 100644 (file)
@@ -31,6 +31,7 @@ COMMENT ON
   CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
   DATABASE <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> |
   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> |
diff --git a/doc/src/sgml/ref/create_extension.sgml b/doc/src/sgml/ref/create_extension.sgml
new file mode 100644 (file)
index 0000000..961cab3
--- /dev/null
@@ -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>
diff --git a/doc/src/sgml/ref/drop_extension.sgml b/doc/src/sgml/ref/drop_extension.sgml
new file mode 100644 (file)
index 0000000..1e09ec4
--- /dev/null
@@ -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>
index eacae71cdc7fb3cc71b510289e29804a58319298..cdf1abfa95639f46cbc027600e7c19ed8a16cd2c 100644 (file)
@@ -1357,7 +1357,6 @@ testdb=&gt;
         </listitem>
       </varlistentry>
 
-
       <varlistentry>
         <term><literal>\du[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
@@ -1371,6 +1370,19 @@ testdb=&gt;
         </listitem>
       </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>
         <term><literal>\edit</> (or <literal>\e</>) <literal> <optional> <replaceable class="parameter">filename</> </optional> <optional> <replaceable class="parameter">line_number</> </optional> </literal></term>
index 6ee8e5bcff82dab89d70cd483aa5c47866e52dc7..47cd01f58e2782973426030991ec9cd60ae8e88b 100644 (file)
@@ -39,6 +39,7 @@
    &alterDatabase;
    &alterDefaultPrivileges;
    &alterDomain;
+   &alterExtension;
    &alterForeignDataWrapper;
    &alterForeignTable;
    &alterFunction;
@@ -78,6 +79,7 @@
    &createConversion;
    &createDatabase;
    &createDomain;
+   &createExtension;
    &createForeignDataWrapper;
    &createForeignTable;
    &createFunction;
    &dropConversion;
    &dropDatabase;
    &dropDomain;
+   &dropExtension;
    &dropForeignDataWrapper;
    &dropForeignTable;
    &dropFunction;
index 2288f1b0e6409235e2e6cbd8accb6a0e12692554..4902c058a96647153172f000d9bbadcb9782ca25 100644 (file)
@@ -3520,9 +3520,8 @@ if TG_OP = 'INSERT' and NEW.col1 = ... then
 
      <listitem>
       <para>
-       Add data and documentation installation location control to <link
-       linkend="xfunc-c-pgxs"><acronym>PGXS</></link> Makefiles
-       (Mark Cave-Ayland)
+       Add data and documentation installation location control to
+       <acronym>PGXS</> Makefiles (Mark Cave-Ayland)
       </para>
      </listitem>
 
index 4ad50ec0cb5494c9ace89e2e9ba73ee6bdb04f20..4f2c23fab7a2780df841112d26c16f9a0df1296d 100644 (file)
@@ -2392,273 +2392,6 @@ concat_text(PG_FUNCTION_ARGS)
 
 &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>
     <title>Composite-type Arguments</title>
 
@@ -3385,4 +3118,68 @@ if (!ptr)
 </programlisting>
     </para>
    </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>
index 7cffde1769231c122be3a48f30c7b411da289386..45aca8dd7f74b45fab8e405eeaca9d42b2a7437f 100644 (file)
@@ -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_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_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_table.h \
        pg_default_acl.h pg_seclabel.h pg_collation.h \
index ba0ea178ac016f21206bc22e9b6ffd6867b25c18..5c5f750a06915aabd9c0653b1820fcd2a3cb543e 100644 (file)
@@ -34,6 +34,7 @@
 #include "catalog/pg_database.h"
 #include "catalog/pg_default_acl.h"
 #include "catalog/pg_depend.h"
+#include "catalog/pg_extension.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
 #include "catalog/pg_foreign_table.h"
@@ -56,6 +57,7 @@
 #include "commands/comment.h"
 #include "commands/dbcommands.h"
 #include "commands/defrem.h"
+#include "commands/extension.h"
 #include "commands/proclang.h"
 #include "commands/schemacmds.h"
 #include "commands/seclabel.h"
@@ -93,6 +95,7 @@ typedef struct
 #define DEPFLAG_NORMAL         0x0002          /* reached via normal dependency */
 #define DEPFLAG_AUTO           0x0004          /* reached via auto dependency */
 #define DEPFLAG_INTERNAL       0x0008          /* reached via internal dependency */
+#define DEPFLAG_EXTENSION      0x0010          /* reached via extension dependency */
 
 
 /* expansible list of ObjectAddresses */
@@ -153,8 +156,8 @@ static const Oid object_classes[MAX_OCLASS] = {
        ForeignDataWrapperRelationId,           /* OCLASS_FDW */
        ForeignServerRelationId,        /* OCLASS_FOREIGN_SERVER */
        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 */
                                break;
                        case DEPENDENCY_INTERNAL:
+                       case DEPENDENCY_EXTENSION:
 
                                /*
                                 * 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
                                 * just ereport here, rather than proceeding, since no other
@@ -726,6 +731,9 @@ findDependentObjects(const ObjectAddress *object,
                        case DEPENDENCY_INTERNAL:
                                subflags = DEPFLAG_INTERNAL;
                                break;
+                       case DEPENDENCY_EXTENSION:
+                               subflags = DEPFLAG_EXTENSION;
+                               break;
                        case DEPENDENCY_PIN:
 
                                /*
@@ -836,10 +844,12 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
 
                /*
                 * 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
-                * RESTRICT mode.
+                * an AUTO, INTERNAL, or EXTENSION dependency, then it's okay to
+                * 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
@@ -1154,6 +1164,10 @@ doDeletion(const ObjectAddress *object)
                        RemoveDefaultACLById(object->objectId);
                        break;
 
+               case OCLASS_EXTENSION:
+                       RemoveExtensionById(object->objectId);
+                       break;
+
                default:
                        elog(ERROR, "unrecognized object class: %u",
                                 object->classId);
@@ -2074,12 +2088,11 @@ getObjectClass(const ObjectAddress *object)
                case UserMappingRelationId:
                        return OCLASS_USER_MAPPING;
 
-               case ForeignTableRelationId:
-                       Assert(object->objectSubId == 0);
-                       return OCLASS_FOREIGN_TABLE;
-
                case DefaultAclRelationId:
                        return OCLASS_DEFACL;
+
+               case ExtensionRelationId:
+                       return OCLASS_EXTENSION;
        }
 
        /* shouldn't get here */
@@ -2687,6 +2700,18 @@ getObjectDescription(const ObjectAddress *object)
                                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:
                        appendStringInfo(&buffer, "unrecognized object %u %u %d",
                                                         object->classId,
index 14c69f3faa88a574ed0afef4ca04d7ae940ea816..d9b272a71220a70fe28518642beb79986b1de5aa 100644 (file)
@@ -1160,7 +1160,8 @@ heap_create_with_catalog(const char *relname,
         * 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
         * 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
         * while bootstrapping.
@@ -1182,6 +1183,8 @@ heap_create_with_catalog(const char *relname,
 
                recordDependencyOnOwner(RelationRelationId, relid, ownerid);
 
+               recordDependencyOnCurrentExtension(&myself);
+
                if (reloftypeid)
                {
                        referenced.classId = TypeRelationId;
index c4608f7f1732e2f9022a29c4c68180dac20c2183..82989acc08839aff0f21cc50f77d43d17c67aff1 100644 (file)
@@ -28,6 +28,7 @@
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_extension.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
@@ -46,6 +47,7 @@
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "commands/defrem.h"
+#include "commands/extension.h"
 #include "commands/proclang.h"
 #include "commands/tablespace.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);
                        break;
                case OBJECT_DATABASE:
+               case OBJECT_EXTENSION:
                case OBJECT_TABLESPACE:
                case OBJECT_ROLE:
                case OBJECT_SCHEMA:
@@ -267,6 +270,9 @@ get_object_address_unqualified(ObjectType objtype, List *qualname)
                        case OBJECT_DATABASE:
                                msg = gettext_noop("database name cannot be qualified");
                                break;
+                       case OBJECT_EXTENSION:
+                               msg = gettext_noop("extension name cannot be qualified");
+                               break;
                        case OBJECT_TABLESPACE:
                                msg = gettext_noop("tablespace name cannot be qualified");
                                break;
@@ -299,6 +305,11 @@ get_object_address_unqualified(ObjectType objtype, List *qualname)
                        address.objectId = get_database_oid(name, false);
                        address.objectSubId = 0;
                        break;
+               case OBJECT_EXTENSION:
+                       address.classId = ExtensionRelationId;
+                       address.objectId = get_extension_oid(name, false);
+                       address.objectSubId = 0;
+                       break;
                case OBJECT_TABLESPACE:
                        address.classId = TableSpaceRelationId;
                        address.objectId = get_tablespace_oid(name, false);
@@ -643,6 +654,9 @@ object_exists(ObjectAddress address)
                case TSConfigRelationId:
                        cache = TSCONFIGOID;
                        break;
+               case ExtensionRelationId:
+                       indexoid = ExtensionOidIndexId;
+                       break;
                default:
                        elog(ERROR, "unrecognized classid: %u", address.classId);
        }
index b2f66d75ac52e7ada95fa26fbd05c4a9b804236d..1ef6a9d24e2dca4bb9e3599d967d1d5fa3807c28 100644 (file)
@@ -132,6 +132,9 @@ ConversionCreate(const char *conname, Oid connamespace,
        recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
                                                        conowner);
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* Post creation hook for new conversion */
        InvokeObjectAccessHook(OAT_POST_CREATE,
                                                   ConversionRelationId, HeapTupleGetOid(tup), 0);
index 370051d91e32feadeb905bbfc9ad21634dc2024c..b2ce148d625ae151ef4fb69863a203bee5ce6dba 100644 (file)
@@ -20,6 +20,8 @@
 #include "catalog/indexing.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
+#include "catalog/pg_extension.h"
+#include "commands/extension.h"
 #include "miscadmin.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
@@ -122,6 +124,28 @@ recordMultipleDependencies(const ObjectAddress *depender,
        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
  * 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
  * object do not change, and links leading from it will be recreated
  * (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
-deleteDependencyRecordsFor(Oid classId, Oid objectId)
+deleteDependencyRecordsFor(Oid classId, Oid objectId,
+                                                  bool skipExtensionDeps)
 {
        long            count = 0;
        Relation        depRel;
@@ -155,6 +184,10 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId)
 
        while (HeapTupleIsValid(tup = systable_getnext(scan)))
        {
+               if (skipExtensionDeps &&
+                       ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
+                       continue;
+
                simple_heap_delete(depRel, &tup->t_self);
                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
  *
index c78aa019bff733160c4bb43fefbed87b93b45572..172f99196cc371c2a90ae9469134842581fe51d9 100644 (file)
@@ -38,6 +38,7 @@ NamespaceCreate(const char *nspName, Oid ownerId)
        Datum           values[Natts_pg_namespace];
        NameData        nname;
        TupleDesc       tupDesc;
+       ObjectAddress myself;
        int                     i;
 
        /* sanity checks */
@@ -73,9 +74,17 @@ NamespaceCreate(const char *nspName, Oid ownerId)
 
        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);
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* Post creation hook for new schema */
        InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0);
 
index c70483a7390afa1205a32fe3203476bac54eb15d..ccd0fe1997def001bb782f1f819d682657d76a64 100644 (file)
@@ -777,7 +777,7 @@ makeOperatorDependencies(HeapTuple tuple)
        myself.objectSubId = 0;
 
        /* 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);
 
        /* Dependency on namespace */
@@ -855,4 +855,7 @@ makeOperatorDependencies(HeapTuple tuple)
        /* Dependency on owner */
        recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
                                                        oper->oprowner);
+
+       /* Dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
 }
index 2ab87d2df5b02e45e5f86f645f139108c749d362..3f3877da286661e45e36a0addcebd1bc2c9f8ed2 100644 (file)
@@ -562,10 +562,11 @@ ProcedureCreate(const char *procedureName,
         * Create dependencies for the new function.  If we are updating an
         * existing function, first delete any existing pg_depend entries.
         * (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)
-               deleteDependencyRecordsFor(ProcedureRelationId, retval);
+               deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
 
        myself.classId = ProcedureRelationId;
        myself.objectId = retval;
@@ -615,6 +616,10 @@ ProcedureCreate(const char *procedureName,
                                                          nnewmembers, newmembers);
        }
 
+       /* dependency on extension */
+       if (!is_update)
+               recordDependencyOnCurrentExtension(&myself);
+
        heap_freetuple(tup);
 
        /* Post creation hook for new function */
index 8ceaab1fb12d939229515d3a4b8171b62e983c40..9b574179ff9a80307d3950686c063cdff58de837 100644 (file)
@@ -481,7 +481,7 @@ TypeCreate(Oid newTypeOid,
  *
  * If rebuild is true, we remove existing dependencies and rebuild them
  * 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
 GenerateTypeDependencies(Oid typeNamespace,
@@ -507,7 +507,7 @@ GenerateTypeDependencies(Oid typeNamespace,
 
        if (rebuild)
        {
-               deleteDependencyRecordsFor(TypeRelationId, typeObjectId);
+               deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true);
                deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0);
        }
 
@@ -521,7 +521,7 @@ GenerateTypeDependencies(Oid typeNamespace,
         * 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
         * 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) &&
                !isImplicitArray)
@@ -532,6 +532,10 @@ GenerateTypeDependencies(Oid typeNamespace,
                recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
                recordDependencyOnOwner(TypeRelationId, typeObjectId, owner);
+
+               /* dependency on extension */
+               if (!rebuild)
+                       recordDependencyOnCurrentExtension(&myself);
        }
 
        /* Normal dependencies on the I/O functions */
index 4fa1453b14f7135644315636bbdb5c59a83144b1..987026c8358c65aa536be67647023eea468cdc3c 100644 (file)
@@ -153,6 +153,13 @@ CREATE VIEW pg_locks AS
 CREATE VIEW pg_cursors AS
     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
     SELECT P.transaction, P.gid, P.prepared,
            U.rolname AS owner, D.datname AS database
index 9d2a7322457dd4b3a67b37b3a03a40b8d18f5e0a..0aadbc56adb20751b698057654bbefd979b05d92 100644 (file)
@@ -14,7 +14,8 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.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 \
        portalcmds.o prepare.o proclang.o \
        schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
index 1c6ae0243e4cb2f20a840dc81af8320717b9e01d..2c9340accf12a33ed346606cac16e81540def39f 100644 (file)
@@ -23,6 +23,7 @@
 #include "commands/conversioncmds.h"
 #include "commands/dbcommands.h"
 #include "commands/defrem.h"
+#include "commands/extension.h"
 #include "commands/proclang.h"
 #include "commands/schemacmds.h"
 #include "commands/tablecmds.h"
@@ -188,6 +189,10 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
                        AlterConversionNamespace(stmt->object, stmt->newschema);
                        break;
 
+               case OBJECT_EXTENSION:
+                       AlterExtensionNamespace(stmt->object, stmt->newschema);
+                       break;
+
                case OBJECT_FUNCTION:
                        AlterFunctionNamespace(stmt->object, stmt->objarg, false,
                                                                   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
- * cases (won't work for tables or functions, objects which have more than 2
- * key-attributes to use when searching for their syscache entries --- we
- * don't want nor need to get this generic here).
+ * cases (won't work for tables, nor other cases where we need to do more
+ * than change the namespace column of a single catalog entry).
  *
  * The AlterFooNamespace() calls just above will call a function whose job
  * is to lookup the arguments for the generic function here.
  *
- * Relation must already by open, it's the responsibility of the caller to
- * close it.
+ * rel: catalog relation containing object (RowExclusiveLock'd by caller)
+ * 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
-AlterObjectNamespace(Relation rel, int cacheId,
-                                        Oid classId, Oid objid, Oid nspOid,
+Oid
+AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
+                                        Oid objid, Oid nspOid,
                                         int Anum_name, int Anum_namespace, int Anum_owner,
-                                        AclObjectKind acl_kind,
-                                        bool superuser_only)
+                                        AclObjectKind acl_kind)
 {
+       Oid                     classId = RelationGetRelid(rel);
        Oid                     oldNspOid;
        Datum       name, namespace;
        bool        isnull;
-       HeapTuple       tup, newtup = NULL;
+       HeapTuple       tup, newtup;
        Datum      *values;
        bool       *nulls;
        bool       *replaces;
 
-       tup = SearchSysCacheCopy1(cacheId, ObjectIdGetDatum(objid));
+       tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
        if (!HeapTupleIsValid(tup)) /* should not happen */
-               elog(ERROR, "cache lookup failed for object %u: %s",
-                        objid, getObjectDescriptionOids(classId, objid));
+               elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
+                        objid, RelationGetRelationName(rel));
 
-       name = heap_getattr(tup, Anum_name, rel->rd_att, &isnull);
-       namespace = heap_getattr(tup, Anum_namespace, rel->rd_att, &isnull);
+       name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
+       Assert(!isnull);
+       namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel), &isnull);
+       Assert(!isnull);
        oldNspOid = DatumGetObjectId(namespace);
 
        /* Check basic namespace related issues */
        CheckSetNamespace(oldNspOid, nspOid, classId, objid);
 
-       /* check for duplicate name (more friendly than unique-index failure) */
-       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 */
+       /* Permission checks ... superusers can always do it */
        if (!superuser())
        {
                Datum       owner;
                Oid                     ownerId;
                AclResult       aclresult;
 
-               if (superuser_only)
+               /* Fail if object does not have an explicit owner */
+               if (Anum_owner <= 0)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                         (errmsg("must be superuser to SET SCHEMA of %s",
                                                         getObjectDescriptionOids(classId, objid)))));
 
                /* 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);
 
                if (!has_privs_of_role(GetUserId(), ownerId))
                        aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind,
                                                   NameStr(*(DatumGetName(name))));
 
-               /* owner must have CREATE privilege on namespace */
-               aclresult = pg_namespace_aclcheck(oldNspOid, GetUserId(), ACL_CREATE);
+               /* User must have CREATE privilege on new namespace */
+               aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
                if (aclresult != ACLCHECK_OK)
                        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));
-       nulls = palloc0(rel->rd_att->natts * sizeof(bool));
-       replaces = palloc0(rel->rd_att->natts * sizeof(bool));
-       values[Anum_namespace - 1] = nspOid;
+       /*
+        * Check for duplicate name (more friendly than unique-index failure).
+        * Since this is just a friendliness check, we can just skip it in cases
+        * where there isn't a suitable syscache available.
+        */
+       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;
-       newtup = heap_modify_tuple(tup, rel->rd_att, values, nulls, replaces);
+       newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
+                                                          values, nulls, replaces);
 
        /* Perform actual update */
        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 */
        changeDependencyFor(classId, objid,
                                                NamespaceRelationId, oldNspOid, nspOid);
+
+       return oldNspOid;
 }
 
 
index 59a439413e1be4e3dd2ad384b93835470294c1b6..4c4f356e79086699518651e6fb8fb52ca96be130 100644 (file)
@@ -1277,7 +1277,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
                        if (relform1->reltoastrelid)
                        {
                                count = deleteDependencyRecordsFor(RelationRelationId,
-                                                                                                  relform1->reltoastrelid);
+                                                                                                  relform1->reltoastrelid,
+                                                                                                  false);
                                if (count != 1)
                                        elog(ERROR, "expected one dependency record for TOAST table, found %ld",
                                                 count);
@@ -1285,7 +1286,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
                        if (relform2->reltoastrelid)
                        {
                                count = deleteDependencyRecordsFor(RelationRelationId,
-                                                                                                  relform2->reltoastrelid);
+                                                                                                  relform2->reltoastrelid,
+                                                                                                  false);
                                if (count != 1)
                                        elog(ERROR, "expected one dependency record for TOAST table, found %ld",
                                                 count);
index 2e8c4df9272dd2433fd1e6b53d930541497ccc20..bbb3f344093b678d305cbd0194afa62b98b6ef0c 100644 (file)
@@ -143,6 +143,12 @@ CommentObject(CommentStmt *stmt)
                                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                         errmsg("must be superuser to comment on procedural language")));
                        break;
+               case OBJECT_EXTENSION:
+                       if (!superuser())
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                                errmsg("must be superuser to comment on extension")));
+                       break;
                case OBJECT_OPCLASS:
                        if (!pg_opclass_ownercheck(address.objectId, GetUserId()))
                                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
index da024df5f0ca82fc7745c4ab9ce0b21769f81fbc..b5e4420ca8d983464a695acfcd87b11cc709b3e5 100644 (file)
@@ -345,12 +345,35 @@ AlterConversionNamespace(List *name, const char *newschema)
        /* get schema OID */
        nspOid = LookupCreationNamespace(newschema);
 
-       AlterObjectNamespace(rel, CONVOID, ConversionRelationId, convOid, nspOid,
+       AlterObjectNamespace(rel, CONVOID, CONNAMENSP,
+                                                convOid, nspOid,
                                                 Anum_pg_conversion_conname,
                                                 Anum_pg_conversion_connamespace,
                                                 Anum_pg_conversion_conowner,
-                                                ACL_KIND_CONVERSION,
-                                                false);
+                                                ACL_KIND_CONVERSION);
 
-       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;
 }
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
new file mode 100644 (file)
index 0000000..5003203
--- /dev/null
@@ -0,0 +1,1401 @@
+/*-------------------------------------------------------------------------
+ *
+ * extension.c
+ *       Commands to manipulate extensions
+ *
+ * Extensions in PostgreSQL allow management of collections of SQL objects.
+ *
+ * All we need internally to manage an extension is an OID so that the
+ * dependent objects can be associated with it.  An extension is created by
+ * populating the pg_extension catalog from a "control" file.
+ * The extension control file is parsed with the same parser we use for
+ * postgresql.conf and recovery.conf.  An extension also has an installation
+ * script file, containing SQL commands to create the extension's objects.
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/commands/extension.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <dirent.h>
+#include <unistd.h>
+
+#include "access/sysattr.h"
+#include "access/xact.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_depend.h"
+#include "catalog/pg_extension.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/alter.h"
+#include "commands/comment.h"
+#include "commands/extension.h"
+#include "commands/trigger.h"
+#include "executor/executor.h"
+#include "funcapi.h"
+#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "tcop/tcopprot.h"
+#include "tcop/utility.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+#include "utils/snapmgr.h"
+#include "utils/tqual.h"
+
+
+bool                   creating_extension = false;
+Oid                            CurrentExtensionObject = InvalidOid;
+
+/*
+ * Internal data structure to hold the results of parsing a control file
+ */
+typedef struct ExtensionControlFile
+{
+       char       *name;                       /* name of the extension */
+       char       *script;                     /* filename of the installation script */
+       char       *version;        /* version ID, if any */
+       char       *comment;            /* comment, if any */
+       char       *schema;                     /* target schema (allowed if !relocatable) */
+       bool            relocatable;    /* is ALTER EXTENSION SET SCHEMA supported? */
+       int                     encoding;               /* encoding of the script file, or -1 */
+       List       *requires;           /* names of prerequisite extensions */
+} ExtensionControlFile;
+
+
+/*
+ * get_extension_oid - given an extension name, look up the OID
+ *
+ * If missing_ok is false, throw an error if extension name not found.  If
+ * true, just return InvalidOid.
+ */
+Oid
+get_extension_oid(const char *extname, bool missing_ok)
+{
+       Oid                     result;
+       Relation        rel;
+       SysScanDesc scandesc;
+       HeapTuple       tuple;
+       ScanKeyData entry[1];
+
+       rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+       ScanKeyInit(&entry[0],
+                               Anum_pg_extension_extname,
+                               BTEqualStrategyNumber, F_NAMEEQ,
+                               CStringGetDatum(extname));
+
+       scandesc = systable_beginscan(rel, ExtensionNameIndexId, true,
+                                                                 SnapshotNow, 1, entry);
+
+       tuple = systable_getnext(scandesc);
+
+       /* We assume that there can be at most one matching tuple */
+       if (HeapTupleIsValid(tuple))
+               result = HeapTupleGetOid(tuple);
+       else
+               result = InvalidOid;
+
+       systable_endscan(scandesc);
+
+       heap_close(rel, AccessShareLock);
+
+       if (!OidIsValid(result) && !missing_ok)
+        ereport(ERROR,
+                (errcode(ERRCODE_UNDEFINED_OBJECT),
+                 errmsg("extension \"%s\" does not exist",
+                        extname)));
+
+       return result;
+}
+
+/*
+ * get_extension_name - given an extension OID, look up the name
+ *
+ * Returns a palloc'd string, or NULL if no such extension.
+ */
+char *
+get_extension_name(Oid ext_oid)
+{
+       char       *result;
+       Relation        rel;
+       SysScanDesc scandesc;
+       HeapTuple       tuple;
+       ScanKeyData entry[1];
+
+       rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+       ScanKeyInit(&entry[0],
+                               ObjectIdAttributeNumber,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(ext_oid));
+
+       scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
+                                                                 SnapshotNow, 1, entry);
+
+       tuple = systable_getnext(scandesc);
+
+       /* We assume that there can be at most one matching tuple */
+       if (HeapTupleIsValid(tuple))
+               result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname));
+       else
+               result = NULL;
+
+       systable_endscan(scandesc);
+
+       heap_close(rel, AccessShareLock);
+
+       return result;
+}
+
+/*
+ * get_extension_schema - given an extension OID, fetch its extnamespace
+ *
+ * Returns InvalidOid if no such extension.
+ */
+static Oid
+get_extension_schema(Oid ext_oid)
+{
+       Oid                     result;
+       Relation        rel;
+       SysScanDesc scandesc;
+       HeapTuple       tuple;
+       ScanKeyData entry[1];
+
+       rel = heap_open(ExtensionRelationId, AccessShareLock);
+
+       ScanKeyInit(&entry[0],
+                               ObjectIdAttributeNumber,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(ext_oid));
+
+       scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
+                                                                 SnapshotNow, 1, entry);
+
+       tuple = systable_getnext(scandesc);
+
+       /* We assume that there can be at most one matching tuple */
+       if (HeapTupleIsValid(tuple))
+               result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;
+       else
+               result = InvalidOid;
+
+       systable_endscan(scandesc);
+
+       heap_close(rel, AccessShareLock);
+
+       return result;
+}
+
+/*
+ * Utility functions to handle extension-related path names
+ */
+static bool
+is_extension_control_filename(const char *filename)
+{
+       const char *extension = strrchr(filename, '.');
+
+       return (extension != NULL) && (strcmp(extension, ".control") == 0);
+}
+
+static char *
+get_extension_control_directory(void)
+{
+       char            sharepath[MAXPGPATH];
+       char       *result;
+
+       get_share_path(my_exec_path, sharepath);
+       result = (char *) palloc(MAXPGPATH);
+       snprintf(result, MAXPGPATH, "%s/contrib", sharepath);
+
+       return result;
+}
+
+static char *
+get_extension_control_filename(const char *extname)
+{
+       char            sharepath[MAXPGPATH];
+       char       *result;
+
+       get_share_path(my_exec_path, sharepath);
+       result = (char *) palloc(MAXPGPATH);
+       snprintf(result, MAXPGPATH, "%s/contrib/%s.control", sharepath, extname);
+
+       return result;
+}
+
+/*
+ * Given a relative pathname such as "name.sql", return the full path to
+ * the script file.  If given an absolute name, just return it.
+ */
+static char *
+get_extension_absolute_path(const char *filename)
+{
+       char            sharepath[MAXPGPATH];
+       char       *result;
+
+       if (is_absolute_path(filename))
+               return pstrdup(filename);
+
+       get_share_path(my_exec_path, sharepath);
+       result = (char *) palloc(MAXPGPATH);
+    snprintf(result, MAXPGPATH, "%s/contrib/%s", sharepath, filename);
+
+       return result;
+}
+
+
+/*
+ * Read the control file for the specified extension.
+ *
+ * The control file is supposed to be very short, half a dozen lines, and
+ * reading it is only allowed to superuser, so we don't worry about
+ * memory allocation risks here.  Also note that we don't worry about
+ * what encoding it's in; all values are expected to be ASCII.
+ */
+static ExtensionControlFile *
+read_extension_control_file(const char *extname)
+{
+       char       *filename = get_extension_control_filename(extname);
+       FILE       *file;
+       ExtensionControlFile *control;
+       ConfigVariable *item,
+                                  *head = NULL,
+                                  *tail = NULL;
+
+       /*
+        * Parse the file content, using GUC's file parsing code
+        */
+       if ((file = AllocateFile(filename, "r")) == NULL)
+               ereport(ERROR,
+                               (errcode_for_file_access(),
+                                errmsg("could not open extension control file \"%s\": %m",
+                                               filename)));
+
+       ParseConfigFp(file, filename, 0, ERROR, &head, &tail);
+
+       FreeFile(file);
+
+       /*
+        * Set up default values.  Pointer fields are initially null.
+        */
+       control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile));
+       control->name = pstrdup(extname);
+       control->relocatable = false;
+       control->encoding = -1;
+
+       /*
+        * Convert the ConfigVariable list into ExtensionControlFile entries.
+        */
+       for (item = head; item != NULL; item = item->next)
+       {
+               if (strcmp(item->name, "script") == 0)
+               {
+                       control->script = pstrdup(item->value);
+               }
+               else if (strcmp(item->name, "version") == 0)
+               {
+                       control->version = pstrdup(item->value);
+               }
+               else if (strcmp(item->name, "comment") == 0)
+               {
+                       control->comment = pstrdup(item->value);
+               }
+               else if (strcmp(item->name, "schema") == 0)
+               {
+                       control->schema = pstrdup(item->value);
+               }
+               else if (strcmp(item->name, "relocatable") == 0)
+               {
+                       if (!parse_bool(item->value, &control->relocatable))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("parameter \"%s\" requires a Boolean value",
+                                                               item->name)));
+               }
+               else if (strcmp(item->name, "encoding") == 0)
+               {
+                       control->encoding = pg_valid_server_encoding(item->value);
+                       if (control->encoding < 0)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                errmsg("\"%s\" is not a valid encoding name",
+                                                               item->value)));
+               }
+               else if (strcmp(item->name, "requires") == 0)
+               {
+                       /* Need a modifiable copy of string */
+                       char       *rawnames = pstrdup(item->value);
+
+                       /* Parse string into list of identifiers */
+                       if (!SplitIdentifierString(rawnames, ',', &control->requires))
+                       {
+                               /* syntax error in name list */
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("parameter \"%s\" must be a list of extension names",
+                                                               item->name)));
+                       }
+               }
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("unrecognized parameter \"%s\" in file \"%s\"",
+                                                       item->name, filename)));
+       }
+
+       FreeConfigVariables(head);
+
+       if (control->relocatable && control->schema != NULL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true")));
+
+       /*
+        * script defaults to ${extension-name}.sql
+        */
+       if (control->script == NULL)
+       {
+               char    script[MAXPGPATH];
+
+               snprintf(script, MAXPGPATH, "%s.sql", control->name);
+               control->script = pstrdup(script);
+       }
+
+       return control;
+}
+
+/*
+ * Read the SQL script into a string, and convert to database encoding
+ */
+static char *
+read_extension_script_file(const ExtensionControlFile *control,
+                                                  const char *filename)
+{
+       int         src_encoding;
+       int         dest_encoding = GetDatabaseEncoding();
+       bytea      *content;
+       char       *src_str;
+       char       *dest_str;
+       int         len;
+
+       content = read_binary_file(filename, 0, -1);
+
+       /* use database encoding if not given */
+       if (control->encoding < 0)
+               src_encoding = dest_encoding;
+       else
+               src_encoding = control->encoding;
+
+       /* make sure that source string is valid in the expected encoding */
+       len = VARSIZE_ANY_EXHDR(content);
+       src_str = VARDATA_ANY(content);
+       pg_verify_mbstr_len(src_encoding, src_str, len, false);
+
+       /* convert the encoding to the database encoding */
+       dest_str = (char *) pg_do_encoding_conversion((unsigned char *) src_str,
+                                                                                                 len,
+                                                                                                 src_encoding,
+                                                                                                 dest_encoding);
+
+       /* if no conversion happened, we have to arrange for null termination */
+       if (dest_str == src_str)
+       {
+               dest_str = (char *) palloc(len + 1);
+               memcpy(dest_str, src_str, len);
+               dest_str[len] = '\0';
+       }
+
+       return dest_str;
+}
+
+/*
+ * Execute given SQL string.
+ *
+ * filename is used only to report errors.
+ *
+ * Note: it's tempting to just use SPI to execute the string, but that does
+ * not work very well.  The really serious problem is that SPI will parse,
+ * analyze, and plan the whole string before executing any of it; of course
+ * this fails if there are any plannable statements referring to objects
+ * created earlier in the script.  A lesser annoyance is that SPI insists
+ * on printing the whole string as errcontext in case of any error, and that
+ * could be very long.
+ */
+static void
+execute_sql_string(const char *sql, const char *filename)
+{
+       List       *raw_parsetree_list;
+       DestReceiver *dest;
+       ListCell   *lc1;
+
+       /*
+        * Parse the SQL string into a list of raw parse trees.
+        */
+       raw_parsetree_list = pg_parse_query(sql);
+
+       /* All output from SELECTs goes to the bit bucket */
+       dest = CreateDestReceiver(DestNone);
+
+       /*
+        * Do parse analysis, rule rewrite, planning, and execution for each raw
+        * parsetree.  We must fully execute each query before beginning parse
+        * analysis on the next one, since there may be interdependencies.
+        */
+       foreach(lc1, raw_parsetree_list)
+       {
+               Node       *parsetree = (Node *) lfirst(lc1);
+               List       *stmt_list;
+               ListCell   *lc2;
+
+               stmt_list = pg_analyze_and_rewrite(parsetree,
+                                                                                  sql,
+                                                                                  NULL,
+                                                                                  0);
+               stmt_list = pg_plan_queries(stmt_list, 0, NULL);
+
+               foreach(lc2, stmt_list)
+               {
+                       Node       *stmt = (Node *) lfirst(lc2);
+
+                       if (IsA(stmt, TransactionStmt))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("transaction control statements are not allowed within an extension script")));
+
+                       CommandCounterIncrement();
+
+                       PushActiveSnapshot(GetTransactionSnapshot());
+
+                       if (IsA(stmt, PlannedStmt) &&
+                               ((PlannedStmt *) stmt)->utilityStmt == NULL)
+                       {
+                               QueryDesc  *qdesc;
+
+                               qdesc = CreateQueryDesc((PlannedStmt *) stmt,
+                                                                               sql,
+                                                                               GetActiveSnapshot(), NULL,
+                                                                               dest, NULL, 0);
+
+                               AfterTriggerBeginQuery();
+                               ExecutorStart(qdesc, 0);
+                               ExecutorRun(qdesc, ForwardScanDirection, 0);
+                               AfterTriggerEndQuery(qdesc->estate);
+                               ExecutorEnd(qdesc);
+
+                               FreeQueryDesc(qdesc);
+                       }
+                       else
+                       {
+                               ProcessUtility(stmt,
+                                                          sql,
+                                                          NULL,
+                                                          false,       /* not top level */
+                                                          dest,
+                                                          NULL);
+                       }
+
+                       PopActiveSnapshot();
+               }
+       }
+
+       /* Be sure to advance the command counter after the last script command */
+       CommandCounterIncrement();
+}
+
+/*
+ * Execute the extension's script file
+ */
+static void
+execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
+                                                List *requiredSchemas,
+                                                const char *schemaName, Oid schemaOid)
+{
+       char       *filename = get_extension_absolute_path(control->script);
+       char       *save_client_min_messages = NULL,
+                          *save_log_min_messages = NULL,
+                          *save_search_path;
+       StringInfoData pathbuf;
+       ListCell   *lc;
+
+       /*
+        * Force client_min_messages and log_min_messages to be at least WARNING,
+        * so that we won't spam the user with useless NOTICE messages from common
+        * script actions like creating shell types.
+        *
+        * We use the equivalent of SET LOCAL to ensure the setting is undone
+        * upon error.
+        */
+       if (client_min_messages < WARNING)
+       {
+               save_client_min_messages =
+                       pstrdup(GetConfigOption("client_min_messages", false));
+               (void) set_config_option("client_min_messages", "warning",
+                                                                PGC_USERSET, PGC_S_SESSION,
+                                                                GUC_ACTION_LOCAL, true);
+       }
+
+       if (log_min_messages < WARNING)
+       {
+               save_log_min_messages =
+                       pstrdup(GetConfigOption("log_min_messages", false));
+               (void) set_config_option("log_min_messages", "warning",
+                                                                PGC_SUSET, PGC_S_SESSION,
+                                                                GUC_ACTION_LOCAL, true);
+       }
+
+       /*
+        * Set up the search path to contain the target schema, then the schemas
+        * of any prerequisite extensions, and nothing else.  In particular this
+        * makes the target schema be the default creation target namespace.
+        *
+        * Note: it might look tempting to use PushOverrideSearchPath for this,
+        * but we cannot do that.  We have to actually set the search_path GUC
+        * in case the extension script examines or changes it.
+        */
+       save_search_path = pstrdup(GetConfigOption("search_path", false));
+
+       initStringInfo(&pathbuf);
+       appendStringInfoString(&pathbuf, quote_identifier(schemaName));
+       foreach(lc, requiredSchemas)
+       {
+               Oid                     reqschema = lfirst_oid(lc);
+               char       *reqname = get_namespace_name(reqschema);
+
+               if (reqname)
+                       appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname));
+       }
+
+       (void) set_config_option("search_path", pathbuf.data,
+                                                        PGC_USERSET, PGC_S_SESSION,
+                                                        GUC_ACTION_LOCAL, true);
+
+       /*
+        * Set creating_extension and related variables so that
+        * recordDependencyOnCurrentExtension and other functions do the right
+        * things.  On failure, ensure we reset these variables.
+        */
+       creating_extension = true;
+       CurrentExtensionObject = extensionOid;
+       PG_TRY();
+       {
+               char *sql = read_extension_script_file(control, filename);
+
+               /*
+                * If it's not relocatable, substitute the target schema name for
+                * occcurrences of @extschema@.
+                *
+                * For a relocatable extension, we just run the script as-is.
+                * There cannot be any need for @extschema@, else it wouldn't
+                * be relocatable.
+                */
+               if (!control->relocatable)
+               {
+                       const char   *qSchemaName = quote_identifier(schemaName);
+
+                       sql = text_to_cstring(
+                               DatumGetTextPP(
+                                       DirectFunctionCall3(replace_text,
+                                                                               CStringGetTextDatum(sql),
+                                                                               CStringGetTextDatum("@extschema@"),
+                                                                               CStringGetTextDatum(qSchemaName))));
+
+               }
+
+               execute_sql_string(sql, filename);
+       }
+       PG_CATCH();
+       {
+               creating_extension = false;
+               CurrentExtensionObject = InvalidOid;
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
+
+       creating_extension = false;
+       CurrentExtensionObject = InvalidOid;
+
+       /*
+        * Restore GUC variables for the remainder of the current transaction.
+        * Again use SET LOCAL, so we won't affect the session value.
+        */
+       (void) set_config_option("search_path", save_search_path,
+                                                        PGC_USERSET, PGC_S_SESSION,
+                                                        GUC_ACTION_LOCAL, true);
+
+       if (save_client_min_messages != NULL)
+               (void) set_config_option("client_min_messages", save_client_min_messages,
+                                                                PGC_USERSET, PGC_S_SESSION,
+                                                                GUC_ACTION_LOCAL, true);
+       if (save_log_min_messages != NULL)
+               (void) set_config_option("log_min_messages", save_log_min_messages,
+                                                                PGC_SUSET, PGC_S_SESSION,
+                                                                GUC_ACTION_LOCAL, true);
+}
+
+/*
+ * CREATE EXTENSION
+ */
+void
+CreateExtension(CreateExtensionStmt *stmt)
+{
+       DefElem    *d_schema = NULL;
+       char       *schemaName;
+       Oid                     schemaOid;
+       Oid                     extowner = GetUserId();
+       ExtensionControlFile *control;
+       List       *requiredExtensions;
+       List       *requiredSchemas;
+       Relation        rel;
+       Datum           values[Natts_pg_extension];
+       bool            nulls[Natts_pg_extension];
+       HeapTuple       tuple;
+       Oid                     extensionOid;
+       ObjectAddress myself;
+       ObjectAddress nsp;
+       ListCell   *lc;
+
+       /* Must be super user */
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("permission denied to create extension \"%s\"",
+                                               stmt->extname),
+                                errhint("Must be superuser to create an extension.")));
+
+       /*
+        * We use global variables to track the extension being created, so we
+        * can create only one extension at the same time.
+        */
+       if (creating_extension)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("nested CREATE EXTENSION is not supported")));
+
+       /*
+        * Check for duplicate extension name.  The unique index on
+        * pg_extension.extname would catch this anyway, and serves as a backstop
+        * in case of race conditions; but this is a friendlier error message.
+        */
+       if (get_extension_oid(stmt->extname, true) != InvalidOid)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                errmsg("extension \"%s\" already exists", stmt->extname)));
+
+       /*
+        * Read the control file.  Note we assume that it does not contain
+        * any non-ASCII data, so there is no need to worry about encoding
+        * at this point.
+        */
+       control = read_extension_control_file(stmt->extname);
+
+       /*
+        * Read the statement option list
+        */
+       foreach(lc, stmt->options)
+       {
+               DefElem    *defel = (DefElem *) lfirst(lc);
+
+               if (strcmp(defel->defname, "schema") == 0)
+               {
+                       if (d_schema)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                                errmsg("conflicting or redundant options")));
+                       d_schema = defel;
+               }
+               else
+                       elog(ERROR, "unrecognized option: %s", defel->defname);
+       }
+
+       /*
+        * Determine the target schema to install the extension into
+        */
+       if (d_schema && d_schema->arg)
+       {
+               /*
+                * User given schema, CREATE EXTENSION ... WITH SCHEMA ...
+                *
+                * It's an error to give a schema different from control->schema if
+                * control->schema is specified.
+                */
+               schemaName = strVal(d_schema->arg);
+
+               if (control->schema != NULL &&
+                       strcmp(control->schema, schemaName) != 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("extension \"%s\" must be installed in schema \"%s\"",
+                                                       control->name,
+                                                       control->schema)));
+
+               /* If the user is giving us the schema name, it must exist already */
+               schemaOid = get_namespace_oid(schemaName, false);
+       }
+       else if (control->schema != NULL)
+       {
+               /*
+                * The extension is not relocatable and the author gave us a schema
+                * for it.  We create the schema here if it does not already exist.
+                */
+               schemaName = control->schema;
+               schemaOid = get_namespace_oid(schemaName, true);
+
+               if (schemaOid == InvalidOid)
+               {
+                       schemaOid = NamespaceCreate(schemaName, extowner);
+                       /* Advance cmd counter to make the namespace visible */
+                       CommandCounterIncrement();
+               }
+       }
+       else
+       {
+               /*
+                * Else, use the current default creation namespace, which is the
+                * first explicit entry in the search_path.
+                */
+               List *search_path = fetch_search_path(false);
+
+               if (search_path == NIL)                         /* probably can't happen */
+                       elog(ERROR, "there is no default creation target");
+               schemaOid = linitial_oid(search_path);
+               schemaName = get_namespace_name(schemaOid);
+               if (schemaName == NULL)                         /* recently-deleted namespace? */
+                       elog(ERROR, "there is no default creation target");
+
+               list_free(search_path);
+       }
+
+       /*
+        * If we didn't already know user is superuser, we would probably want
+        * to do pg_namespace_aclcheck(schemaOid, extowner, ACL_CREATE) here.
+        */
+
+       /*
+        * Look up the prerequisite extensions, and build lists of their OIDs
+        * and the OIDs of their target schemas.
+        */
+       requiredExtensions = NIL;
+       requiredSchemas = NIL;
+       foreach(lc, control->requires)
+       {
+               char       *curreq = (char *) lfirst(lc);
+               Oid                     reqext;
+               Oid                     reqschema;
+
+               /*
+                * We intentionally don't use get_extension_oid's default error
+                * message here, because it would be confusing in this context.
+                */
+               reqext = get_extension_oid(curreq, true);
+               if (!OidIsValid(reqext))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                        errmsg("required extension \"%s\" is not installed",
+                                                       curreq)));
+               reqschema = get_extension_schema(reqext);
+               requiredExtensions = lappend_oid(requiredExtensions, reqext);
+               requiredSchemas = lappend_oid(requiredSchemas, reqschema);
+       }
+
+       /*
+        * Insert new tuple into pg_extension.
+        */
+       rel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+       memset(values, 0, sizeof(values));
+       memset(nulls, 0, sizeof(nulls));
+
+       values[Anum_pg_extension_extname - 1] =
+               DirectFunctionCall1(namein, CStringGetDatum(control->name));
+       values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extowner);
+       values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid);
+       values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(control->relocatable);
+
+       if (control->version == NULL)
+               nulls[Anum_pg_extension_extversion - 1] = true;
+       else
+               values[Anum_pg_extension_extversion - 1] =
+                       CStringGetTextDatum(control->version);
+
+       nulls[Anum_pg_extension_extconfig - 1] = true;
+       nulls[Anum_pg_extension_extcondition - 1] = true;
+
+       tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+       extensionOid = simple_heap_insert(rel, tuple);
+       CatalogUpdateIndexes(rel, tuple);
+
+       heap_freetuple(tuple);
+       heap_close(rel, RowExclusiveLock);
+
+       /*
+        * Apply any comment on extension
+        */
+       if (control->comment != NULL)
+               CreateComments(extensionOid, ExtensionRelationId, 0, control->comment);
+
+       /*
+        * Record dependencies on owner, schema, and prerequisite extensions
+        */
+       recordDependencyOnOwner(ExtensionRelationId, extensionOid, extowner);
+
+       myself.classId = ExtensionRelationId;
+       myself.objectId = extensionOid;
+       myself.objectSubId = 0;
+
+       nsp.classId = NamespaceRelationId;
+       nsp.objectId = schemaOid;
+       nsp.objectSubId = 0;
+
+       recordDependencyOn(&myself, &nsp, DEPENDENCY_NORMAL);
+
+       foreach(lc, requiredExtensions)
+       {
+               Oid                     reqext = lfirst_oid(lc);
+               ObjectAddress otherext;
+
+               otherext.classId = ExtensionRelationId;
+               otherext.objectId = reqext;
+               otherext.objectSubId = 0;
+
+               recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
+       }
+
+       /*
+        * Finally, execute the extension script to create the member objects
+        */
+       execute_extension_script(extensionOid, control, requiredSchemas,
+                                                        schemaName, schemaOid);
+}
+
+
+/*
+ *     RemoveExtensions
+ *             Implements DROP EXTENSION.
+ */
+void
+RemoveExtensions(DropStmt *drop)
+{
+       ObjectAddresses *objects;
+       ListCell   *cell;
+
+       /*
+        * First we identify all the extensions, then we delete them in a single
+        * performMultipleDeletions() call.  This is to avoid unwanted DROP
+        * RESTRICT errors if one of the extensions depends on another.
+        */
+       objects = new_object_addresses();
+
+       foreach(cell, drop->objects)
+       {
+               List       *names = (List *) lfirst(cell);
+               char       *extensionName;
+               Oid                     extensionId;
+               ObjectAddress object;
+
+               if (list_length(names) != 1)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("extension name cannot be qualified")));
+               extensionName = strVal(linitial(names));
+
+               extensionId = get_extension_oid(extensionName, drop->missing_ok);
+
+               if (!OidIsValid(extensionId))
+               {
+                       ereport(NOTICE,
+                                       (errmsg("extension \"%s\" does not exist, skipping",
+                                                       extensionName)));
+                       continue;
+               }
+
+               /*
+                * Permission check.  For now, insist on superuser-ness; later we
+                * might want to relax that to being owner of the extension.
+                */
+               if (!superuser())
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                        errmsg("permission denied to drop extension \"%s\"",
+                                                       extensionName),
+                                        errhint("Must be superuser to drop an extension.")));
+
+               object.classId = ExtensionRelationId;
+               object.objectId = extensionId;
+               object.objectSubId = 0;
+
+               add_exact_object_address(&object, objects);
+       }
+
+       /*
+        * Do the deletions.  Objects contained in the extension(s) are removed by
+        * means of their dependency links to the extensions.
+        */
+       performMultipleDeletions(objects, drop->behavior);
+
+       free_object_addresses(objects);
+}
+
+
+/*
+ * Guts of extension deletion.
+ *
+ * All we need do here is remove the pg_extension tuple itself.  Everything
+ * else is taken care of by the dependency infrastructure.
+ */
+void
+RemoveExtensionById(Oid extId)
+{
+       Relation        rel;
+       SysScanDesc scandesc;
+       HeapTuple       tuple;
+       ScanKeyData entry[1];
+
+       rel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+       ScanKeyInit(&entry[0],
+                               ObjectIdAttributeNumber,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(extId));
+       scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
+                                                                 SnapshotNow, 1, entry);
+
+       tuple = systable_getnext(scandesc);
+
+       /* We assume that there can be at most one matching tuple */
+       if (HeapTupleIsValid(tuple))
+               simple_heap_delete(rel, &tuple->t_self);
+
+       systable_endscan(scandesc);
+
+       heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * This function lists the extensions available in the control directory
+ * (each of which might or might not actually be installed).  We parse each
+ * available control file and report the interesting fields.
+ *
+ * The system view pg_available_extensions provides a user interface to this
+ * SRF, adding information about whether the extensions are installed in the
+ * current DB.
+ */
+Datum
+pg_available_extensions(PG_FUNCTION_ARGS)
+{
+       ReturnSetInfo      *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+       TupleDesc                       tupdesc;
+       Tuplestorestate    *tupstore;
+       MemoryContext           per_query_ctx;
+       MemoryContext           oldcontext;
+       char                       *location;
+       DIR                                *dir;
+       struct dirent      *de;
+
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                (errmsg("must be superuser to list available extensions"))));
+
+       /* check to see if caller supports us returning a tuplestore */
+       if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("set-valued function called in context that cannot accept a set")));
+       if (!(rsinfo->allowedModes & SFRM_Materialize))
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("materialize mode required, but it is not " \
+                                               "allowed in this context")));
+
+       /* Build a tuple descriptor for our result type */
+       if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+               elog(ERROR, "return type must be a row type");
+
+       per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+       oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+       tupstore = tuplestore_begin_heap(true, false, work_mem);
+       rsinfo->returnMode = SFRM_Materialize;
+       rsinfo->setResult = tupstore;
+       rsinfo->setDesc = tupdesc;
+
+       MemoryContextSwitchTo(oldcontext);
+
+       location = get_extension_control_directory();
+       dir  = AllocateDir(location);
+
+       /*
+        * If the control directory doesn't exist, we want to silently return
+        * an empty set.  Any other error will be reported by ReadDir.
+        */
+       if (dir == NULL && errno == ENOENT)
+       {
+               /* do nothing */
+       }
+       else
+       {
+               while ((de = ReadDir(dir, location)) != NULL)
+               {
+                       ExtensionControlFile *control;
+                       char       *extname;
+                       Datum           values[4];
+                       bool            nulls[4];
+
+                       if (!is_extension_control_filename(de->d_name))
+                               continue;
+
+                       /* extract extension name from 'name.control' filename */
+                       extname = pstrdup(de->d_name);
+                       *strrchr(extname, '.') = '\0';
+
+                       control = read_extension_control_file(extname);
+
+                       memset(values, 0, sizeof(values));
+                       memset(nulls, 0, sizeof(nulls));
+
+                       /* name */
+                       values[0] = DirectFunctionCall1(namein,
+                                                                                       CStringGetDatum(control->name));
+                       /* version */
+                       if (control->version == NULL)
+                               nulls[1] = true;
+                       else
+                               values[1] = CStringGetTextDatum(control->version);
+                       /* relocatable */
+                       values[2] = BoolGetDatum(control->relocatable);
+                       /* comment */
+                       if (control->comment == NULL)
+                               nulls[3] = true;
+                       else
+                               values[3] = CStringGetTextDatum(control->comment);
+
+                       tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+               }
+
+               FreeDir(dir);
+       }
+
+       /* clean up and return the tuplestore */
+       tuplestore_donestoring(tupstore);
+
+       return (Datum) 0;
+}
+
+/*
+ * pg_extension_config_dump
+ *
+ * Record information about a configuration table that belongs to an
+ * extension being created, but whose contents should be dumped in whole
+ * or in part during pg_dump.
+ */
+Datum
+pg_extension_config_dump(PG_FUNCTION_ARGS)
+{
+       Oid                     tableoid = PG_GETARG_OID(0);
+       text       *wherecond = PG_GETARG_TEXT_P(1);
+       char       *tablename;
+       Relation        extRel;
+       ScanKeyData     key[1];
+       SysScanDesc     extScan;
+       HeapTuple       extTup;
+       Datum           arrayDatum;
+       Datum           elementDatum;
+       int                     arrayIndex;
+       bool            isnull;
+       Datum           repl_val[Natts_pg_extension];
+       bool            repl_null[Natts_pg_extension];
+       bool            repl_repl[Natts_pg_extension];
+       ArrayType  *a;
+
+       /*
+        * We only allow this to be called from an extension's SQL script.
+        * We shouldn't need any permissions check beyond that.
+        */
+       if (!creating_extension)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("pg_extension_config_dump() can only be called "
+                                               "from a SQL script executed by CREATE EXTENSION")));
+
+       /*
+        * Check that the table exists and is a member of the extension being
+        * created.  This ensures that we don't need to register a dependency
+        * to protect the extconfig entry.
+        */
+       tablename = get_rel_name(tableoid);
+       if (tablename == NULL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_TABLE),
+                                errmsg("OID %u does not refer to a table", tableoid)));
+       if (getExtensionOfObject(RelationRelationId, tableoid) !=
+               CurrentExtensionObject)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("table \"%s\" is not a member of the extension being created",
+                                               tablename)));
+
+       /*
+        * Add the table OID and WHERE condition to the extension's extconfig
+        * and extcondition arrays.
+        */
+
+       /* Find the pg_extension tuple */
+       extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+       ScanKeyInit(&key[0],
+                               ObjectIdAttributeNumber,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(CurrentExtensionObject));
+
+       extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
+                                                                SnapshotNow, 1, key);
+
+       extTup = systable_getnext(extScan);
+
+       if (!HeapTupleIsValid(extTup)) /* should not happen */
+               elog(ERROR, "extension with oid %u does not exist",
+                        CurrentExtensionObject);
+
+       memset(repl_val, 0, sizeof(repl_val));
+       memset(repl_null, false, sizeof(repl_null));
+       memset(repl_repl, false, sizeof(repl_repl));
+
+       /* Build or modify the extconfig value */
+       elementDatum = ObjectIdGetDatum(tableoid);
+
+       arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig,
+                                                         RelationGetDescr(extRel), &isnull);
+       if (isnull)
+       {
+               a = construct_array(&elementDatum, 1,
+                                                       OIDOID,
+                                                       sizeof(Oid), true, 'i');
+       }
+       else
+       {
+               a = DatumGetArrayTypeP(arrayDatum);
+               Assert(ARR_ELEMTYPE(a) == OIDOID);
+               Assert(ARR_NDIM(a) == 1);
+               Assert(ARR_LBOUND(a)[0] == 1);
+
+               arrayIndex = ARR_DIMS(a)[0] + 1; /* add after end */
+
+               a = array_set(a, 1, &arrayIndex,
+                                         elementDatum,
+                                         false,
+                                         -1 /* varlena array */ ,
+                                         sizeof(Oid) /* OID's typlen */ ,
+                                         true /* OID's typbyval */ ,
+                                         'i' /* OID's typalign */ );
+       }
+       repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
+       repl_repl[Anum_pg_extension_extconfig - 1] = true;
+
+       /* Build or modify the extcondition value */
+       elementDatum = PointerGetDatum(wherecond);
+
+       arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition,
+                                                         RelationGetDescr(extRel), &isnull);
+       if (isnull)
+       {
+               a = construct_array(&elementDatum, 1,
+                                                       TEXTOID,
+                                                       -1, false, 'i');
+       }
+       else
+       {
+               a = DatumGetArrayTypeP(arrayDatum);
+               Assert(ARR_ELEMTYPE(a) == TEXTOID);
+               Assert(ARR_NDIM(a) == 1);
+               Assert(ARR_LBOUND(a)[0] == 1);
+
+               arrayIndex = ARR_DIMS(a)[0] + 1; /* add after end */
+
+               a = array_set(a, 1, &arrayIndex,
+                                         elementDatum,
+                                         false,
+                                         -1 /* varlena array */ ,
+                                         -1 /* TEXT's typlen */ ,
+                                         false /* TEXT's typbyval */ ,
+                                         'i' /* TEXT's typalign */ );
+       }
+       repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
+       repl_repl[Anum_pg_extension_extcondition - 1] = true;
+
+       extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel),
+                                                          repl_val, repl_null, repl_repl);
+
+       simple_heap_update(extRel, &extTup->t_self, extTup);
+       CatalogUpdateIndexes(extRel, extTup);
+
+       systable_endscan(extScan);
+
+       heap_close(extRel, RowExclusiveLock);
+
+       PG_RETURN_VOID();
+}
+
+/*
+ * Execute ALTER EXTENSION SET SCHEMA
+ */
+void
+AlterExtensionNamespace(List *names, const char *newschema)
+{
+       char       *extensionName;
+       Oid                     extensionOid;
+       Oid                     nspOid;
+       Oid                     oldNspOid = InvalidOid;
+       Relation        extRel;
+       ScanKeyData     key[2];
+       SysScanDesc     extScan;
+       HeapTuple       extTup;
+       Form_pg_extension extForm;
+       Relation        depRel;
+       SysScanDesc     depScan;
+       HeapTuple       depTup;
+
+       if (list_length(names) != 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("extension name cannot be qualified")));
+       extensionName = strVal(linitial(names));
+
+       extensionOid = get_extension_oid(extensionName, false);
+
+       nspOid = LookupCreationNamespace(newschema);
+
+       /* this might later become an ownership test */
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                (errmsg("must be superuser to use ALTER EXTENSION"))));
+
+       /* Locate the pg_extension tuple */
+       extRel = heap_open(ExtensionRelationId, RowExclusiveLock);
+
+       ScanKeyInit(&key[0],
+                               ObjectIdAttributeNumber,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(extensionOid));
+
+       extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
+                                                                SnapshotNow, 1, key);
+
+       extTup = systable_getnext(extScan);
+
+       if (!HeapTupleIsValid(extTup)) /* should not happen */
+               elog(ERROR, "extension with oid %u does not exist", extensionOid);
+
+       /* Copy tuple so we can modify it below */
+       extTup = heap_copytuple(extTup);
+       extForm = (Form_pg_extension) GETSTRUCT(extTup);
+
+       systable_endscan(extScan);
+
+       /*
+        * If the extension is already in the target schema, just silently
+        * do nothing.
+        */
+       if (extForm->extnamespace == nspOid)
+       {
+               heap_close(extRel, RowExclusiveLock);
+               return;
+       }
+
+       /* Check extension is supposed to be relocatable */
+       if (!extForm->extrelocatable)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("extension \"%s\" does not support SET SCHEMA",
+                                               NameStr(extForm->extname))));
+
+       /*
+        * Scan pg_depend to find objects that depend directly on the extension,
+        * and alter each one's schema.
+        */
+       depRel = heap_open(DependRelationId, AccessShareLock);
+
+       ScanKeyInit(&key[0],
+                               Anum_pg_depend_refclassid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(ExtensionRelationId));
+       ScanKeyInit(&key[1],
+                               Anum_pg_depend_refobjid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(extensionOid));
+
+       depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
+                                                                SnapshotNow, 2, key);
+
+       while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
+       {
+               Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
+               ObjectAddress dep;
+               Oid dep_oldNspOid;
+
+               /*
+                * Ignore non-membership dependencies.  (Currently, the only other
+                * case we could see here is a normal dependency from another
+                * extension.)
+                */
+               if (pg_depend->deptype != DEPENDENCY_EXTENSION)
+                       continue;
+
+               dep.classId = pg_depend->classid;
+               dep.objectId = pg_depend->objid;
+               dep.objectSubId = pg_depend->objsubid;
+
+               if (dep.objectSubId != 0)               /* should not happen */
+                       elog(ERROR, "extension should not have a sub-object dependency");
+
+               dep_oldNspOid = AlterObjectNamespace_oid(dep.classId,
+                                                                                                dep.objectId,
+                                                                                                nspOid);
+
+               /*
+                * Remember previous namespace of first object that has one
+                */
+               if (oldNspOid == InvalidOid && dep_oldNspOid != InvalidOid)
+                       oldNspOid = dep_oldNspOid;
+
+               /*
+                * If not all the objects had the same old namespace (ignoring any
+                * that are not in namespaces), complain.
+                */
+               if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("extension \"%s\" does not support SET SCHEMA",
+                                                       NameStr(extForm->extname)),
+                                        errdetail("%s is not in the extension's schema \"%s\"",
+                                                          getObjectDescription(&dep),
+                                                          get_namespace_name(oldNspOid))));
+       }
+
+       systable_endscan(depScan);
+
+       relation_close(depRel, AccessShareLock);
+
+       /* Now adjust pg_extension.extnamespace */
+       extForm->extnamespace = nspOid;
+
+       simple_heap_update(extRel, &extTup->t_self, extTup);
+       CatalogUpdateIndexes(extRel, extTup);
+
+       heap_close(extRel, RowExclusiveLock);
+
+       /* update dependencies to point to the new schema */
+       changeDependencyFor(ExtensionRelationId, extensionOid,
+                                               NamespaceRelationId, oldNspOid, nspOid);
+}
index 3a0ea9a632395544f0714976b9441d9815c06301..a2b5358e16fe7e9daf178fcd4f9d2b2115a9360c 100644 (file)
@@ -342,6 +342,8 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
        Oid                     fdwvalidator;
        Datum           fdwoptions;
        Oid                     ownerId;
+       ObjectAddress myself;
+       ObjectAddress referenced;
 
        /* Must be super user */
        if (!superuser())
@@ -401,15 +403,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 
        heap_freetuple(tuple);
 
+       /* record dependencies */
+       myself.classId = ForeignDataWrapperRelationId;
+       myself.objectId = fdwId;
+       myself.objectSubId = 0;
+
        if (fdwvalidator)
        {
-               ObjectAddress myself;
-               ObjectAddress referenced;
-
-               myself.classId = ForeignDataWrapperRelationId;
-               myself.objectId = fdwId;
-               myself.objectSubId = 0;
-
                referenced.classId = ProcedureRelationId;
                referenced.objectId = fdwvalidator;
                referenced.objectSubId = 0;
@@ -418,6 +418,9 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 
        recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* Post creation hook for new foreign data wrapper */
        InvokeObjectAccessHook(OAT_POST_CREATE,
                                                   ForeignDataWrapperRelationId, fdwId, 0);
@@ -691,7 +694,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 
        heap_freetuple(tuple);
 
-       /* Add dependency on FDW and owner */
+       /* record dependencies */
        myself.classId = ForeignServerRelationId;
        myself.objectId = srvId;
        myself.objectSubId = 0;
@@ -703,6 +706,9 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 
        recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* Post creation hook for new foreign server */
        InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
 
@@ -974,8 +980,13 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
        if (OidIsValid(useId))
+       {
                /* Record the mapped user dependency */
                recordDependencyOnOwner(UserMappingRelationId, umId, useId);
+       }
+
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
 
        /* Post creation hook for new user mapping */
        InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
index dad65ee8ffaf06eab4b98aa17d732f65dde037f7..3f25b3bf02a232e4a57bf5679a1e66970335b5c4 100644 (file)
@@ -1762,6 +1762,9 @@ CreateCast(CreateCastStmt *stmt)
                recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
        }
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* Post creation hook for new cast */
        InvokeObjectAccessHook(OAT_POST_CREATE,
                                                   CastRelationId, myself.objectId, 0);
@@ -1875,13 +1878,7 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
                                           const char *newschema)
 {
        Oid                     procOid;
-       Oid                     oldNspOid;
        Oid                     nspOid;
-       HeapTuple       tup;
-       Relation        procRel;
-       Form_pg_proc proc;
-
-       procRel = heap_open(ProcedureRelationId, RowExclusiveLock);
 
        /* get function OID */
        if (isagg)
@@ -1889,20 +1886,33 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
        else
                procOid = LookupFuncNameTypeNames(name, argtypes, false);
 
-       /* check permissions on function */
-       if (!pg_proc_ownercheck(procOid, GetUserId()))
-               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
-                                          NameListToString(name));
+       /* get schema OID and check its permissions */
+       nspOid = LookupCreationNamespace(newschema);
+
+       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));
        if (!HeapTupleIsValid(tup))
                elog(ERROR, "cache lookup failed for function %u", procOid);
        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 */
-       nspOid = LookupCreationNamespace(newschema);
+       oldNspOid = proc->pronamespace;
 
        /* common checks on switching namespaces */
        CheckSetNamespace(oldNspOid, nspOid, ProcedureRelationId, procOid);
@@ -1916,7 +1926,7 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
                                (errcode(ERRCODE_DUPLICATE_FUNCTION),
                                 errmsg("function \"%s\" already exists in schema \"%s\"",
                                                NameStr(proc->proname),
-                                               newschema)));
+                                               get_namespace_name(nspOid))));
 
        /* OK, modify the pg_proc row */
 
@@ -1930,11 +1940,13 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
        if (changeDependencyFor(ProcedureRelationId, procOid,
                                                        NamespaceRelationId, oldNspOid, nspOid) != 1)
                elog(ERROR, "failed to change schema dependency for function \"%s\"",
-                        NameListToString(name));
+                        NameStr(proc->proname));
 
        heap_freetuple(tup);
 
        heap_close(procRel, RowExclusiveLock);
+
+       return oldNspOid;
 }
 
 
index 662b9420387f90bf3c9b6e656ed7b90583319d98..68072dd42184de43b18143d333cc6196eaf0fdf8 100644 (file)
@@ -309,6 +309,9 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
        /* dependency on owner */
        recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* Post creation hook for new operator family */
        InvokeObjectAccessHook(OAT_POST_CREATE,
                                                   OperatorFamilyRelationId, opfamilyoid, 0);
@@ -709,6 +712,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
        /* dependency on owner */
        recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* Post creation hook for new operator class */
        InvokeObjectAccessHook(OAT_POST_CREATE,
                                                   OperatorClassRelationId, opclassoid, 0);
@@ -1997,28 +2003,48 @@ AlterOpClassNamespace(List *name, char *access_method, const char *newschema)
 {
        Oid                     amOid;
        Relation        rel;
-       Oid                     oid;
+       Oid                     opclassOid;
        Oid                     nspOid;
 
        amOid = get_am_oid(access_method, false);
 
        rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
 
-       /* Look up the opclass. */
-       oid = get_opclass_oid(amOid, name, false);
+       /* Look up the opclass */
+       opclassOid = get_opclass_oid(amOid, name, false);
 
        /* get schema OID */
        nspOid = LookupCreationNamespace(newschema);
 
-       AlterObjectNamespace(rel, CLAOID, OperatorClassRelationId,
-                                                oid, nspOid,
-                                                Anum_pg_opfamily_opfname,
-                                                Anum_pg_opfamily_opfnamespace,
-                                                Anum_pg_opfamily_opfowner,
-                                                ACL_KIND_OPCLASS,
-                                                false);
+       AlterObjectNamespace(rel, CLAOID, -1,
+                                                opclassOid, nspOid,
+                                                Anum_pg_opclass_opcname,
+                                                Anum_pg_opclass_opcnamespace,
+                                                Anum_pg_opclass_opcowner,
+                                                ACL_KIND_OPCLASS);
 
-       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;
        Relation        rel;
+       Oid                     opfamilyOid;
        Oid                     nspOid;
-       Oid                     oid;
 
        amOid = get_am_oid(access_method, false);
 
        rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
 
        /* Look up the opfamily */
-       oid = get_opfamily_oid(amOid, name, false);
+       opfamilyOid = get_opfamily_oid(amOid, name, false);
 
        /* get schema OID */
        nspOid = LookupCreationNamespace(newschema);
 
-       AlterObjectNamespace(rel, OPFAMILYOID, OperatorFamilyRelationId,
-                                                oid, nspOid,
+       AlterObjectNamespace(rel, OPFAMILYOID, -1,
+                                                opfamilyOid, nspOid,
                                                 Anum_pg_opfamily_opfname,
                                                 Anum_pg_opfamily_opfnamespace,
                                                 Anum_pg_opfamily_opfowner,
-                                                ACL_KIND_OPFAMILY,
-                                                false);
+                                                ACL_KIND_OPFAMILY);
 
-       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;
 }
index 35bb76162d2c9d394abfcd694c4567722fe50898..b4374a62f4f0830bfb7b91b8326b314584260884 100644 (file)
@@ -477,12 +477,32 @@ AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
        /* get schema OID */
        nspOid = LookupCreationNamespace(newschema);
 
-       AlterObjectNamespace(rel, OPEROID, OperatorRelationId, operOid, nspOid,
+       AlterObjectNamespace(rel, OPEROID, -1,
+                                                operOid, nspOid,
                                                 Anum_pg_operator_oprname,
                                                 Anum_pg_operator_oprnamespace,
                                                 Anum_pg_operator_oprowner,
-                                                ACL_KIND_OPER,
-                                                false);
+                                                ACL_KIND_OPER);
 
-       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;
 }
index 3860105b266d87b804dd136b7587f3bfd35f5849..b36f31ee6d52f5619fbb5eadea4fd9ad73c33a64 100644 (file)
@@ -388,20 +388,25 @@ create_proc_lang(const char *languageName, bool replace,
         * Create dependencies for the new language.  If we are updating an
         * existing language, first delete any existing pg_depend entries.
         * (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.objectId = HeapTupleGetOid(tup);
        myself.objectSubId = 0;
 
        if (is_update)
-               deleteDependencyRecordsFor(myself.classId, myself.objectId);
+               deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
 
        /* dependency on owner of language */
        if (!is_update)
                recordDependencyOnOwner(myself.classId, myself.objectId,
                                                                languageOwner);
 
+       /* dependency on extension */
+       if (!is_update)
+               recordDependencyOnCurrentExtension(&myself);
+
        /* dependency on the PL handler function */
        referenced.classId = ProcedureRelationId;
        referenced.objectId = handlerOid;
index c0a4e6f954a28fee2c1177d658b23d766f18275d..f67e9b9b16290f496f8d4343cbfd6a0f19c5f544 100644 (file)
@@ -6851,6 +6851,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                        case OCLASS_FOREIGN_SERVER:
                        case OCLASS_USER_MAPPING:
                        case OCLASS_DEFACL:
+                       case OCLASS_EXTENSION:
 
                                /*
                                 * We don't expect any of these sorts of objects to depend on
index 7afd2f896ed4b017d8c516603dd84e0d1c69b2cd..81f129dff6bc4f11a290657d6179e13f11fafffc 100644 (file)
@@ -135,6 +135,9 @@ makeParserDependencies(HeapTuple tuple)
        referenced.objectSubId = 0;
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* dependencies on functions */
        referenced.classId = ProcedureRelationId;
        referenced.objectSubId = 0;
@@ -414,12 +417,33 @@ AlterTSParserNamespace(List *name, const char *newschema)
        /* get schema OID */
        nspOid = LookupCreationNamespace(newschema);
 
-       AlterObjectNamespace(rel, TSPARSEROID, TSParserRelationId, prsId, nspOid,
+       AlterObjectNamespace(rel, TSPARSEROID, TSPARSERNAMENSP,
+                                                prsId, nspOid,
                                                 Anum_pg_ts_parser_prsname,
                                                 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 -----------------------*/
@@ -447,6 +471,9 @@ makeDictionaryDependencies(HeapTuple tuple)
        /* dependency on owner */
        recordDependencyOnOwner(myself.classId, myself.objectId, dict->dictowner);
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* dependency on template */
        referenced.classId = TSTemplateRelationId;
        referenced.objectId = dict->dicttemplate;
@@ -668,14 +695,35 @@ AlterTSDictionaryNamespace(List *name, const char *newschema)
        /* get schema OID */
        nspOid = LookupCreationNamespace(newschema);
 
-       AlterObjectNamespace(rel, TSDICTOID, TSDictionaryRelationId, dictId, nspOid,
+       AlterObjectNamespace(rel, TSDICTOID, TSDICTNAMENSP,
+                                                dictId, nspOid,
                                                 Anum_pg_ts_dict_dictname,
                                                 Anum_pg_ts_dict_dictnamespace,
                                                 Anum_pg_ts_dict_dictowner,
-                                                ACL_KIND_TSDICTIONARY,
-                                                true);
+                                                ACL_KIND_TSDICTIONARY);
 
-       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;
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
+       /* dependency on extension */
+       recordDependencyOnCurrentExtension(&myself);
+
        /* dependencies on functions */
        referenced.classId = ProcedureRelationId;
        referenced.objectSubId = 0;
@@ -1177,13 +1228,33 @@ AlterTSTemplateNamespace(List *name, const char *newschema)
        /* get schema OID */
        nspOid = LookupCreationNamespace(newschema);
 
-       AlterObjectNamespace(rel, TSTEMPLATEOID, TSTemplateRelationId,
+       AlterObjectNamespace(rel, TSTEMPLATEOID, TSTEMPLATENAMENSP,
                                                 tmplId, nspOid,
                                                 Anum_pg_ts_template_tmplname,
                                                 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.objectSubId = 0;
 
-       /* for ALTER case, first flush old dependencies */
+       /* for ALTER case, first flush old dependencies, except extension deps */
        if (removeOld)
        {
-               deleteDependencyRecordsFor(myself.classId, myself.objectId);
+               deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
                deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
        }
 
@@ -1336,6 +1407,10 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
        /* dependency on owner */
        recordDependencyOnOwner(myself.classId, myself.objectId, cfg->cfgowner);
 
+       /* dependency on extension */
+       if (!removeOld)
+               recordDependencyOnCurrentExtension(&myself);
+
        /* dependency on parser */
        referenced.classId = TSParserRelationId;
        referenced.objectId = cfg->cfgparser;
@@ -1603,14 +1678,35 @@ AlterTSConfigurationNamespace(List *name, const char *newschema)
        /* get schema OID */
        nspOid = LookupCreationNamespace(newschema);
 
-       AlterObjectNamespace(rel, TSCONFIGOID, TSConfigRelationId, cfgId, nspOid,
+       AlterObjectNamespace(rel, TSCONFIGOID, TSCONFIGNAMENSP,
+                                                cfgId, nspOid,
                                                 Anum_pg_ts_config_cfgname,
                                                 Anum_pg_ts_config_cfgnamespace,
                                                 Anum_pg_ts_config_cfgowner,
-                                                ACL_KIND_TSCONFIGURATION,
-                                                false);
+                                                ACL_KIND_TSCONFIGURATION);
 
-       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;
 }
 
 /*
index 25d0f3596e14c47da1775b431897d5f4f167a369..fb9d67a30a5f214aab73e74e1ca147e73bf761b8 100644 (file)
@@ -2780,20 +2780,27 @@ AlterTypeNamespace(List *names, const char *newschema)
        TypeName   *typename;
        Oid                     typeOid;
        Oid                     nspOid;
-       Oid                     elemOid;
 
        /* Make a TypeName so we can use standard type lookup machinery */
        typename = makeTypeNameFromNameList(names);
        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 */
        if (!pg_type_ownercheck(typeOid, GetUserId()))
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
                                           format_type_be(typeOid));
 
-       /* get schema OID and check its permissions */
-       nspOid = LookupCreationNamespace(newschema);
-
        /* don't allow direct alteration of array types */
        elemOid = get_element_type(typeOid);
        if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
@@ -2805,7 +2812,7 @@ AlterTypeNamespace(List *names, const char *newschema)
                                                 format_type_be(elemOid))));
 
        /* 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
  * a table type.  ALTER TABLE has to be used to move a table to a new
  * namespace.
+ *
+ * Returns the type's old namespace OID.
  */
-void
+Oid
 AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
                                                   bool isImplicitArray,
                                                   bool errorOnTableType)
@@ -2928,4 +2937,6 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
        /* Recursively alter the associated array type, if any */
        if (OidIsValid(arrayOid))
                AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
+
+       return oldNspOid;
 }
index 9b2c874d6d0001b0c8246cb74f14679d351513ae..851186146dd78f0f40f619380a6d0b1eb8c5ecf6 100644 (file)
@@ -3239,6 +3239,17 @@ _copyAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *from)
        return newnode;
 }
 
+static CreateExtensionStmt *
+_copyCreateExtensionStmt(CreateExtensionStmt *from)
+{
+       CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt);
+
+       COPY_STRING_FIELD(extname);
+       COPY_NODE_FIELD(options);
+
+       return newnode;
+}
+
 static CreateFdwStmt *
 _copyCreateFdwStmt(CreateFdwStmt *from)
 {
@@ -4238,6 +4249,9 @@ copyObject(void *from)
                case T_AlterTableSpaceOptionsStmt:
                        retval = _copyAlterTableSpaceOptionsStmt(from);
                        break;
+               case T_CreateExtensionStmt:
+                       retval = _copyCreateExtensionStmt(from);
+                       break;
                case T_CreateFdwStmt:
                        retval = _copyCreateFdwStmt(from);
                        break;
index 837eafaaccb673b046cea646b36d367c11246282..00d23ccfa560b47612d3f0c8a11eae6d5292070b 100644 (file)
@@ -1645,6 +1645,15 @@ _equalAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *a,
        return true;
 }
 
+static bool
+_equalCreateExtensionStmt(CreateExtensionStmt *a, CreateExtensionStmt *b)
+{
+       COMPARE_STRING_FIELD(extname);
+       COMPARE_NODE_FIELD(options);
+
+       return true;
+}
+
 static bool
 _equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b)
 {
@@ -2845,6 +2854,9 @@ equal(void *a, void *b)
                case T_AlterTableSpaceOptionsStmt:
                        retval = _equalAlterTableSpaceOptionsStmt(a, b);
                        break;
+               case T_CreateExtensionStmt:
+                       retval = _equalCreateExtensionStmt(a, b);
+                       break;
                case T_CreateFdwStmt:
                        retval = _equalCreateFdwStmt(a, b);
                        break;
index a1bcf02f5be530bec49ccf689b06b1afd761d598..4c4536b9be31c5ab8fb9ac07eda91e0abf6d1fb9 100644 (file)
@@ -191,7 +191,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
                AlterDefaultPrivilegesStmt DefACLAction
                AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
                ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
-               CreateDomainStmt CreateGroupStmt CreateOpClassStmt
+               CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
                CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
                CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
                CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
@@ -227,9 +227,9 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <dbehavior>      opt_drop_behavior
 
 %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
-                               transaction_mode_item
+                               transaction_mode_item create_extension_opt_item
 
 %type <ival>   opt_lock lock_type cast_context
 %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
 
        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
        FREEZE FROM FULL FUNCTION FUNCTIONS
@@ -692,6 +693,7 @@ stmt :
                        | CreateCastStmt
                        | CreateConversionStmt
                        | CreateDomainStmt
+                       | CreateExtensionStmt
                        | CreateFdwStmt
                        | CreateForeignServerStmt
                        | 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:
@@ -4340,11 +4373,12 @@ drop_type:      TABLE                                                                   { $$ = OBJECT_TABLE; }
                        | SEQUENCE                                                              { $$ = OBJECT_SEQUENCE; }
                        | VIEW                                                                  { $$ = OBJECT_VIEW; }
                        | INDEX                                                                 { $$ = OBJECT_INDEX; }
-                       | TYPE_P                                                                { $$ = OBJECT_TYPE; }
                        | FOREIGN TABLE                                                 { $$ = OBJECT_FOREIGN_TABLE; }
+                       | TYPE_P                                                                { $$ = OBJECT_TYPE; }
                        | DOMAIN_P                                                              { $$ = OBJECT_DOMAIN; }
                        | CONVERSION_P                                                  { $$ = OBJECT_CONVERSION; }
                        | SCHEMA                                                                { $$ = OBJECT_SCHEMA; }
+                       | EXTENSION                                                             { $$ = OBJECT_EXTENSION; }
                        | TEXT_P SEARCH PARSER                                  { $$ = OBJECT_TSPARSER; }
                        | TEXT_P SEARCH DICTIONARY                              { $$ = OBJECT_TSDICTIONARY; }
                        | TEXT_P SEARCH TEMPLATE                                { $$ = OBJECT_TSTEMPLATE; }
@@ -4398,7 +4432,7 @@ opt_restart_seqs:
  *
  *     COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
  *                                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 TEMPLATE | TEXT SEARCH CONFIGURATION |
  *                                FOREIGN TABLE ] <objname> |
@@ -4577,6 +4611,7 @@ comment_type:
                        | VIEW                                                          { $$ = OBJECT_VIEW; }
                        | CONVERSION_P                                          { $$ = OBJECT_CONVERSION; }
                        | TABLESPACE                                            { $$ = OBJECT_TABLESPACE; }
+                       | EXTENSION                                             { $$ = OBJECT_EXTENSION; }
                        | ROLE                                                          { $$ = OBJECT_ROLE; }
                        | FOREIGN TABLE                                         { $$ = OBJECT_FOREIGN_TABLE; }
                ;
@@ -6271,6 +6306,14 @@ AlterObjectSchemaStmt:
                                        n->newschema = $6;
                                        $$ = (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
                                {
                                        AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
@@ -11462,6 +11505,7 @@ unreserved_keyword:
                        | EXCLUSIVE
                        | EXECUTE
                        | EXPLAIN
+                       | EXTENSION
                        | EXTERNAL
                        | FAMILY
                        | FIRST_P
index 99c397b4f201a8eabd2563a359cf1125431c2ac3..fecc4e27fa37d3323ba72ad57d090296d02fa014 100644 (file)
@@ -143,7 +143,7 @@ InsertRule(char *rulname,
 
        /* If replacing, get rid of old dependencies and make new ones */
        if (is_update)
-               deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId);
+               deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId, false);
 
        /*
         * Install dependency on rule's relation to ensure it will go away on
index af2eba01d614642f28cd12a0c213739f6d33962b..10a4438995f94d0a0d8412616528046bf7ec9089 100644 (file)
@@ -32,6 +32,7 @@
 #include "commands/defrem.h"
 #include "commands/discard.h"
 #include "commands/explain.h"
+#include "commands/extension.h"
 #include "commands/lockcmds.h"
 #include "commands/portalcmds.h"
 #include "commands/prepare.h"
@@ -210,6 +211,7 @@ check_xact_readonly(Node *parsetree)
                case T_ReassignOwnedStmt:
                case T_AlterTSDictionaryStmt:
                case T_AlterTSConfigurationStmt:
+               case T_CreateExtensionStmt:
                case T_CreateFdwStmt:
                case T_AlterFdwStmt:
                case T_DropFdwStmt:
@@ -594,6 +596,10 @@ standard_ProcessUtility(Node *parsetree,
                        AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree);
                        break;
 
+               case T_CreateExtensionStmt:
+                       CreateExtension((CreateExtensionStmt *) parsetree);
+                       break;
+
                case T_CreateFdwStmt:
                        CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
                        break;
@@ -673,6 +679,10 @@ standard_ProcessUtility(Node *parsetree,
                                                RemoveTSConfigurations(stmt);
                                                break;
 
+                                       case OBJECT_EXTENSION:
+                                               RemoveExtensions(stmt);
+                                               break;
+
                                        default:
                                                elog(ERROR, "unrecognized drop object type: %d",
                                                         (int) stmt->removeType);
@@ -1544,6 +1554,10 @@ CreateCommandTag(Node *parsetree)
                        tag = "ALTER TABLESPACE";
                        break;
 
+               case T_CreateExtensionStmt:
+                       tag = "CREATE EXTENSION";
+                       break;
+
                case T_CreateFdwStmt:
                        tag = "CREATE FOREIGN DATA WRAPPER";
                        break;
@@ -1626,6 +1640,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_FOREIGN_TABLE:
                                        tag = "DROP FOREIGN TABLE";
                                        break;
+                               case OBJECT_EXTENSION:
+                                       tag = "DROP EXTENSION";
+                                       break;
                                default:
                                        tag = "???";
                        }
@@ -1741,6 +1758,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_DOMAIN:
                                        tag = "ALTER DOMAIN";
                                        break;
+                               case OBJECT_EXTENSION:
+                                       tag = "ALTER EXTENSION";
+                                       break;
                                case OBJECT_OPERATOR:
                                        tag = "ALTER OPERATOR";
                                        break;
@@ -2382,6 +2402,10 @@ GetCommandLogLevel(Node *parsetree)
                        lev = LOGSTMT_DDL;
                        break;
 
+               case T_CreateExtensionStmt:
+                       lev = LOGSTMT_DDL;
+                       break;
+
                case T_CreateFdwStmt:
                case T_AlterFdwStmt:
                case T_DropFdwStmt:
index 93bc401c2d157931e0ba6f9627f559f052a799f9..c3ec98aa5e26e5993ff4fb1f788cee046a934665 100644 (file)
@@ -82,22 +82,16 @@ convert_and_check_filename(text *arg)
 /*
  * 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 *
-read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read)
+bytea *
+read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read)
 {
        bytea      *buf;
        size_t          nbytes;
        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)
        {
@@ -146,7 +140,6 @@ read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read)
        SET_VARSIZE(buf, nbytes + VARHDRSZ);
 
        FreeFile(file);
-       pfree(filename);
 
        return buf;
 }
@@ -156,9 +149,11 @@ read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read)
  * in the database encoding.
  */
 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 */
        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);
        int64           seek_offset = PG_GETARG_INT64(1);
        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)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 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)
 {
        text       *filename_t = PG_GETARG_TEXT_P(0);
+       char       *filename;
+
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                (errmsg("must be superuser to read files"))));
+
+       filename = convert_and_check_filename(filename_t);
 
-       PG_RETURN_TEXT_P(read_text_file(filename_t, 0, -1));
+       PG_RETURN_TEXT_P(read_text_file(filename, 0, -1));
 }
 
 /*
@@ -205,13 +216,21 @@ pg_read_binary_file(PG_FUNCTION_ARGS)
        text       *filename_t = PG_GETARG_TEXT_P(0);
        int64           seek_offset = PG_GETARG_INT64(1);
        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)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 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)
 {
        text       *filename_t = PG_GETARG_TEXT_P(0);
+       char       *filename;
+
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                (errmsg("must be superuser to read files"))));
+
+       filename = convert_and_check_filename(filename_t);
 
-       PG_RETURN_BYTEA_P(read_binary_file(filename_t, 0, -1));
+       PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1));
 }
 
 /*
index 55cc3126e1e34b4c0162289bf62aa7afe907cd6d..cc0db663200f09309280e460f8021c751dea0d2e 100644 (file)
@@ -84,6 +84,7 @@ getSchemaData(int *numTablesPtr)
        RuleInfo   *ruleinfo;
        ProcLangInfo *proclanginfo;
        CastInfo   *castinfo;
+       ExtensionInfo *extinfo;
        OpclassInfo *opcinfo;
        OpfamilyInfo *opfinfo;
        ConvInfo   *convinfo;
@@ -100,6 +101,7 @@ getSchemaData(int *numTablesPtr)
        int                     numRules;
        int                     numProcLangs;
        int                     numCasts;
+       int                     numExtensions;
        int                     numOpclasses;
        int                     numOpfamilies;
        int                     numConversions;
@@ -197,6 +199,11 @@ getSchemaData(int *numTablesPtr)
                write_msg(NULL, "reading type casts\n");
        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 */
        if (g_verbose)
                write_msg(NULL, "finding inheritance relationships\n");
index 49c570016ad092699ce913e903a760a07ba26f7f..dec96bc0253119e77c454febc377ec0abcbf3fdf 100644 (file)
@@ -161,6 +161,7 @@ static int  findSecLabels(Archive *fout, Oid classoid, Oid objoid,
 static int     collectSecLabels(Archive *fout, SecLabelItem **items);
 static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
 static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
+static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
 static void dumpType(Archive *fout, TypeInfo *tyinfo);
 static void dumpBaseType(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 getDomainConstraints(TypeInfo *tyinfo);
 static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
+static void makeTableDataInfo(TableInfo *tbinfo, bool oids);
 static void getTableDataFKConstraints(void);
 static char *format_function_arguments(FuncInfo *finfo, char *funcargs);
 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.
+        *
+        * (In 9.1 and later, this also marks extension member objects as
+        * not to be dumped.)
         */
        getDependencies();
 
@@ -1232,6 +1237,23 @@ dumpTableData_copy(Archive *fout, void *dcontext)
                                                                                 classname),
                                                  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
        {
                appendPQExpBuffer(q, "COPY %s %s TO stdout;",
@@ -1356,6 +1378,8 @@ dumpTableData_insert(Archive *fout, void *dcontext)
                                                  fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
                                                                                 classname));
        }
+       if (tdinfo->filtercond)
+               appendPQExpBuffer(q, " %s", tdinfo->filtercond);
 
        res = PQexec(g_conn, q->data);
        check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK);
@@ -1480,10 +1504,15 @@ static void
 dumpTableData(Archive *fout, TableDataInfo *tdinfo)
 {
        TableInfo  *tbinfo = tdinfo->tdtable;
-       PQExpBuffer copyBuf = createPQExpBuffer();
+       PQExpBuffer copyBuf;
        DataDumperPtr dumpFn;
        char       *copyStmt;
 
+       if (!tdinfo->dobj.dump)
+               return;
+
+       copyBuf = createPQExpBuffer();
+
        if (!dump_inserts)
        {
                /* Dump/restore using COPY */
@@ -1539,30 +1568,40 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids)
                        && no_unlogged_table_data)
                        continue;
 
-               if (tblinfo[i].dobj.dump)
-               {
-                       TableDataInfo *tdinfo;
+               if (tblinfo[i].dobj.dump && tblinfo[i].dataObj == NULL)
+                       makeTableDataInfo(&(tblinfo[i]), oids);
+       }
+}
 
-                       tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo));
+/*
+ * Make a dumpable object for the data of this specific table
+ */
+static void
+makeTableDataInfo(TableInfo *tbinfo, bool oids)
+{
+       TableDataInfo *tdinfo;
 
-                       tdinfo->dobj.objType = DO_TABLE_DATA;
+       tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo));
 
-                       /*
-                        * 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;
-               }
-       }
+       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;
 }
 
 /*
@@ -2584,6 +2623,123 @@ findNamespace(Oid nsoid, Oid objoid)
        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:
  *       read all types in the system catalogs and return them in the
@@ -5044,6 +5200,9 @@ getProcLangs(int *numProcLangs)
                else
                        planginfo[i].lanowner = strdup("");
 
+               /* Assume it should be dumped (getDependencies may override this) */
+               planginfo[i].dobj.dump = true;
+
                if (g_fout->remoteVersion < 70300)
                {
                        /*
@@ -5151,6 +5310,9 @@ getCasts(int *numCasts)
                castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
                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
                 * 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:
                        dumpNamespace(fout, (NamespaceInfo *) dobj);
                        break;
+               case DO_EXTENSION:
+                       dumpExtension(fout, (ExtensionInfo *) dobj);
+                       break;
                case DO_TYPE:
                        dumpType(fout, (TypeInfo *) dobj);
                        break;
@@ -6734,6 +6899,56 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
        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
  *       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   *validatorInfo = NULL;
 
-       if (dataOnly)
+       /* Skip if not to be dumped */
+       if (!plang->dobj.dump || dataOnly)
                return;
 
        /*
@@ -8418,7 +8634,8 @@ dumpCast(Archive *fout, CastInfo *cast)
        TypeInfo   *sourceInfo;
        TypeInfo   *targetInfo;
 
-       if (dataOnly)
+       /* Skip if not to be dumped */
+       if (!cast->dobj.dump || dataOnly)
                return;
 
        if (OidIsValid(cast->castfunc))
@@ -12747,6 +12964,24 @@ getDependencies(void)
                else
                        /* normal case */
                        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);
index 43fd1ade27f0bf1ec8127c5526e65bdb14343ab3..f0e9ae1e06442d75a3d89c717ec89e8d941f7718 100644 (file)
@@ -89,6 +89,7 @@ typedef enum
 {
        /* When modifying this enum, update priority tables in pg_dump_sort.c! */
        DO_NAMESPACE,
+       DO_EXTENSION,
        DO_TYPE,
        DO_SHELL_TYPE,
        DO_FUNC,
@@ -139,6 +140,12 @@ typedef struct _namespaceInfo
        char       *nspacl;
 } NamespaceInfo;
 
+typedef struct _extensionInfo
+{
+       DumpableObject dobj;
+       char       *namespace;          /* schema containing extension's objects */
+} ExtensionInfo;
+
 typedef struct _typeInfo
 {
        DumpableObject dobj;
@@ -288,6 +295,8 @@ typedef struct _tableDataInfo
        DumpableObject dobj;
        TableInfo  *tdtable;            /* link to table to dump */
        bool            oids;                   /* include OIDs in data? */
+       bool            ext_config;             /* is table an extension config table? */
+       char       *filtercond;         /* WHERE condition to limit rows dumped */
 } TableDataInfo;
 
 typedef struct _indxInfo
@@ -513,6 +522,7 @@ extern void sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs);
  * version specific routines
  */
 extern NamespaceInfo *getNamespaces(int *numNamespaces);
+extern ExtensionInfo *getExtensions(int *numExtensions);
 extern TypeInfo *getTypes(int *numTypes);
 extern FuncInfo *getFuncs(int *numFuncs);
 extern AggInfo *getAggregates(int *numAggregates);
index fe5cf56e695f3c0c8dde159fc9b302c43e6b1e72..f1c1c65e6cfa763890ff6e254d07cd2b8e257448 100644 (file)
@@ -22,13 +22,14 @@ static const char *modulename = gettext_noop("sorter");
  * Sort priority for object types when dumping a pre-7.3 database.
  * Objects are sorted by priority levels, and within an equal priority level
  * by OID.     (This is a relatively crude hack to provide semi-reasonable
- * behavior for old databases without full dependency info.)  Note: text
- * search, foreign-data, and default ACL objects can't really happen here,
+ * behavior for old databases without full dependency info.)  Note: extensions,
+ * text search, foreign-data, and default ACL objects can't really happen here,
  * so the rather bogus priorities for them don't matter.
  */
 static const int oldObjectTypePriority[] =
 {
        1,                                                      /* DO_NAMESPACE */
+       1,                                                      /* DO_EXTENSION */
        2,                                                      /* DO_TYPE */
        2,                                                      /* DO_SHELL_TYPE */
        2,                                                      /* DO_FUNC */
@@ -66,34 +67,35 @@ static const int oldObjectTypePriority[] =
 static const int newObjectTypePriority[] =
 {
        1,                                                      /* DO_NAMESPACE */
-       3,                                                      /* DO_TYPE */
-       3,                                                      /* DO_SHELL_TYPE */
-       4,                                                      /* DO_FUNC */
-       5,                                                      /* DO_AGG */
-       6,                                                      /* DO_OPERATOR */
-       7,                                                      /* DO_OPCLASS */
-       7,                                                      /* DO_OPFAMILY */
-       9,                                                      /* DO_CONVERSION */
-       16,                                                     /* DO_TABLE */
-       18,                                                     /* DO_ATTRDEF */
-       23,                                                     /* DO_INDEX */
-       24,                                                     /* DO_RULE */
-       25,                                                     /* DO_TRIGGER */
-       22,                                                     /* DO_CONSTRAINT */
-       26,                                                     /* DO_FK_CONSTRAINT */
+       3,                                                      /* DO_EXTENSION */
+       4,                                                      /* DO_TYPE */
+       4,                                                      /* DO_SHELL_TYPE */
+       5,                                                      /* DO_FUNC */
+       6,                                                      /* DO_AGG */
+       7,                                                      /* DO_OPERATOR */
+       8,                                                      /* DO_OPCLASS */
+       8,                                                      /* DO_OPFAMILY */
+       10,                                                     /* DO_CONVERSION */
+       17,                                                     /* DO_TABLE */
+       19,                                                     /* DO_ATTRDEF */
+       24,                                                     /* DO_INDEX */
+       25,                                                     /* DO_RULE */
+       26,                                                     /* DO_TRIGGER */
+       23,                                                     /* DO_CONSTRAINT */
+       27,                                                     /* DO_FK_CONSTRAINT */
        2,                                                      /* DO_PROCLANG */
-       8,                                                      /* DO_CAST */
-       20,                                                     /* DO_TABLE_DATA */
-       17,                                                     /* DO_DUMMY_TYPE */
-       10,                                                     /* DO_TSPARSER */
-       12,                                                     /* DO_TSDICT */
-       11,                                                     /* DO_TSTEMPLATE */
-       13,                                                     /* DO_TSCONFIG */
-       14,                                                     /* DO_FDW */
-       15,                                                     /* DO_FOREIGN_SERVER */
-       27,                                                     /* DO_DEFAULT_ACL */
-       19,                                                     /* DO_BLOB */
-       21                                                      /* DO_BLOB_DATA */
+       9,                                                      /* DO_CAST */
+       21,                                                     /* DO_TABLE_DATA */
+       18,                                                     /* DO_DUMMY_TYPE */
+       11,                                                     /* DO_TSPARSER */
+       13,                                                     /* DO_TSDICT */
+       12,                                                     /* DO_TSTEMPLATE */
+       14,                                                     /* DO_TSCONFIG */
+       15,                                                     /* DO_FDW */
+       16,                                                     /* DO_FOREIGN_SERVER */
+       28,                                                     /* DO_DEFAULT_ACL */
+       20,                                                     /* DO_BLOB */
+       22                                                      /* DO_BLOB_DATA */
 };
 
 
@@ -1023,6 +1025,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
                                         "SCHEMA %s  (ID %d OID %u)",
                                         obj->name, obj->dumpId, obj->catId.oid);
                        return;
+               case DO_EXTENSION:
+                       snprintf(buf, bufsize,
+                                        "EXTENSION %s  (ID %d OID %u)",
+                                        obj->name, obj->dumpId, obj->catId.oid);
+                       return;
                case DO_TYPE:
                        snprintf(buf, bufsize,
                                         "TYPE %s  (ID %d OID %u)",
index 301dc11bcc2f7101663267d2e8fbad5f4ad497b8..a80678c2c3fc012f3722d39d7e48ac09b90dc9f9 100644 (file)
@@ -495,6 +495,12 @@ exec_command(const char *cmd,
                                                break;
                                }
                                break;
+                       case 'x':          /* Extensions */
+                               if (show_verbose)
+                                       success = listExtensionContents(pattern);
+                               else
+                                       success = listExtensions(pattern);
+                               break;
                        default:
                                status = PSQL_CMD_UNKNOWN;
                }
index d6c6cf1f15e6109c577fc28499d2ad3cda91e528..0342eb55bdce7b41f8e2a6c2f915ba01635fd7bd 100644 (file)
@@ -38,6 +38,7 @@ static bool describeOneTSConfig(const char *oid, const char *nspname,
                                        const char *cfgname,
                                        const char *pnspname, const char *prsname);
 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)
        {
-               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);
                return true;
        }
@@ -3718,6 +3719,167 @@ listForeignTables(const char *pattern, bool verbose)
        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
  *
index 4e80bcf41f86d8be9f3fb07b9522b17d0616e44e..4b690b3b7070af5efddd690c6f4037071d0a22b1 100644 (file)
@@ -87,4 +87,10 @@ extern bool listForeignTables(const char *pattern, bool verbose);
 /* \dL */
 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 */
index 2647b1081fab8d1855b553395501c418090429aa..c44079e03431c4bf3dd2a74bdb2caa5cf5e8ca20 100644 (file)
@@ -222,6 +222,7 @@ slashUsage(unsigned short int pager)
        fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
        fprintf(output, _("  \\dv[S+] [PATTERN]      list views\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, _("  \\sf[+] FUNCNAME        show a function's definition\n"));
        fprintf(output, _("  \\z      [PATTERN]      same as \\dp\n"));
index 84c68a7bff283240d179bf2e67ec52a1a85abd7a..1c9623de1e56440540c96f4d3fd2692c805f2abb 100644 (file)
@@ -578,6 +578,16 @@ static const SchemaQuery Query_for_list_of_views = {
 "   FROM pg_catalog.pg_proc "\
 "  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
  * 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},
        {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
        {"DOMAIN", NULL, &Query_for_list_of_domains},
+       {"EXTENSION", Query_for_list_of_extensions},
        {"FOREIGN DATA WRAPPER", NULL, NULL},
        {"FOREIGN TABLE", NULL, NULL},
        {"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)
        {
                static const char *const list_ALTER[] =
-               {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
-                       "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
-               "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
+               {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
+                "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
+                "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);
        }
@@ -838,6 +852,11 @@ psql_completion(char *text, int start, int end)
                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 */
        else if (pg_strcasecmp(prev2_wd, "ALTER") == 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)
                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 */
        else if (pg_strcasecmp(prev2_wd, "CREATE") == 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 &&
                          (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
                           pg_strcasecmp(prev2_wd, "DOMAIN") == 0 ||
+                          pg_strcasecmp(prev2_wd, "EXTENSION") == 0 ||
                           pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
                           pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
                           pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 ||
index 3defe0641f2f54e0bc0758cabebba6b05e75a4a2..019cd8fab5a6c09d66dfc22aeb8d63155f48c471 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201102083
+#define CATALOG_VERSION_NO     201102084
 
 #endif
index c6ab313edf8c57691a6dce4eb2ceecbab39968c9..4d7ff8853d1c0c26c61d4aa0eff5bfb204e11869 100644 (file)
  * Example: a trigger that's created to enforce a foreign-key constraint
  * 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
  * 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
@@ -64,6 +70,7 @@ typedef enum DependencyType
        DEPENDENCY_NORMAL = 'n',
        DEPENDENCY_AUTO = 'a',
        DEPENDENCY_INTERNAL = 'i',
+       DEPENDENCY_EXTENSION = 'e',
        DEPENDENCY_PIN = 'p'
 } DependencyType;
 
@@ -137,8 +144,8 @@ typedef enum ObjectClass
        OCLASS_FDW,                                     /* pg_foreign_data_wrapper */
        OCLASS_FOREIGN_SERVER,          /* pg_foreign_server */
        OCLASS_USER_MAPPING,            /* pg_user_mapping */
-       OCLASS_FOREIGN_TABLE,           /* pg_foreign_table */
        OCLASS_DEFACL,                          /* pg_default_acl */
+       OCLASS_EXTENSION,           /* pg_extension */
        MAX_OCLASS                                      /* MUST BE LAST */
 } ObjectClass;
 
@@ -193,12 +200,17 @@ extern void recordMultipleDependencies(const ObjectAddress *depender,
                                                   int nreferenced,
                                                   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,
                                        Oid refClassId, Oid oldRefObjectId,
                                        Oid newRefObjectId);
 
+extern Oid     getExtensionOfObject(Oid classId, Oid objectId);
+
 extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId);
 
 extern void markSequenceUnowned(Oid seqId);
index 866942cf9a310740392f72f6ad44eba1d84e9013..4118e64542430ee47788d30fb2914baf41910583 100644 (file)
@@ -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));
 #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 */
 BUILD_INDICES
 
diff --git a/src/include/catalog/pg_extension.h b/src/include/catalog/pg_extension.h
new file mode 100644 (file)
index 0000000..0ad47b0
--- /dev/null
@@ -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 */
index 836574355cd476839e96a805ae0c3deb481a82b0..9e6ec3dda50ca2ad41c82382358592fb6da235c7 100644 (file)
@@ -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_ ));
 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 */
 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");
index 74d5132636c86ada0421904e84c0584f59b83b0b..21731685f5c6f79a164bcda42ca3cba1ee44e321 100644 (file)
 
 extern void ExecRenameStmt(RenameStmt *stmt);
 extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
-extern void AlterObjectNamespace(Relation rel, int cacheId,
-                                                                Oid classId, Oid objid, Oid nspId,
-                                                                int Anum_name, int Anum_namespace, int Anum_owner,
-                                                                AclObjectKind acl_kind,
-                                                                bool superuser_only);
+extern Oid     AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid);
+extern Oid     AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
+                                        Oid objid, Oid nspOid,
+                                        int Anum_name, int Anum_namespace, int Anum_owner,
+                                        AclObjectKind acl_kind);
 extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt);
 
 #endif   /* ALTER_H */
index 6156c4a94e62b3acd05b502628ad19b65ab30e59..f77023ffe323b15ffe689952e79827e09989b73b 100644 (file)
@@ -23,5 +23,6 @@ extern void RenameConversion(List *name, const char *newname);
 extern void AlterConversionOwner(List *name, Oid newOwnerId);
 extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
 extern void AlterConversionNamespace(List *name, const char *newschema);
+extern Oid     AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
 
 #endif   /* CONVERSIONCMDS_H */
index 01f271bff43f3c3d8560cb9169e85afe9545d41b..157ee394614a0c1c86d9cbc09f522150d283582e 100644 (file)
@@ -66,6 +66,7 @@ extern void DropCast(DropCastStmt *stmt);
 extern void DropCastById(Oid castOid);
 extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
                                           const char *newschema);
+extern Oid     AlterFunctionNamespace_oid(Oid procOid, Oid nspOid);
 extern void ExecuteDoStmt(DoStmt *stmt);
 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);
 extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
 extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
+extern Oid     AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
 
 /* commands/aggregatecmds.c */
 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_oid(Oid opclassOid, Oid newOwnerId);
 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_oid(Oid opfamilyOid, Oid newOwnerId);
 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_opclass_oid(Oid amID, List *opclassname, 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 RenameTSParser(List *oldname, const char *newname);
 extern void AlterTSParserNamespace(List *name, const char *newschema);
+extern Oid     AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
 extern void RemoveTSParsers(DropStmt *drop);
 extern void RemoveTSParserById(Oid prsId);
 
@@ -121,10 +126,12 @@ extern void RemoveTSDictionaryById(Oid dictId);
 extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
 extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
 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 RenameTSTemplate(List *oldname, const char *newname);
 extern void AlterTSTemplateNamespace(List *name, const char *newschema);
+extern Oid     AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
 extern void RemoveTSTemplates(DropStmt *stmt);
 extern void RemoveTSTemplateById(Oid tmplId);
 
@@ -135,6 +142,7 @@ extern void RemoveTSConfigurationById(Oid cfgId);
 extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
 extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
 extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
+extern Oid     AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
 
 extern text *serialize_deflist(List *deflist);
 extern List *deserialize_deflist(Datum txt);
diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h
new file mode 100644 (file)
index 0000000..10d0893
--- /dev/null
@@ -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 */
index b13363aaf7e383e4b3328cf93ad784c3acd7f4f3..1b20296934f53e4aac5aa298c0e733e31184513d 100644 (file)
@@ -41,7 +41,8 @@ extern void AlterTypeOwner(List *names, Oid newOwnerId);
 extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
                                           bool hasDependEntry);
 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 errorOnTableType);
 
index 63b9dd80be9ab20e2b601e5313aa42ea97eb99fd..1ca5f1ef9a19e559b2ab814323ed4023e2a5dd29 100644 (file)
@@ -355,6 +355,7 @@ typedef enum NodeTag
        T_AlterTableSpaceOptionsStmt,
        T_SecLabelStmt,
        T_CreateForeignTableStmt,
+       T_CreateExtensionStmt,
 
        /*
         * TAGS FOR PARSE TREE NODES (parsenodes.h)
index b68fe10990a114d9319649bbdc7a50c6d53040d7..5de4dbd5ec152f0543516ab2e9a3bffa7466a729 100644 (file)
@@ -1073,6 +1073,7 @@ typedef enum ObjectType
        OBJECT_CONVERSION,
        OBJECT_DATABASE,
        OBJECT_DOMAIN,
+       OBJECT_EXTENSION,
        OBJECT_FDW,
        OBJECT_FOREIGN_SERVER,
        OBJECT_FOREIGN_TABLE,
@@ -1533,6 +1534,18 @@ typedef struct AlterTableSpaceOptionsStmt
        bool            isReset;
 } AlterTableSpaceOptionsStmt;
 
+/* ----------------------
+ *             Create Extension Statement
+ * ----------------------
+ */
+
+typedef struct CreateExtensionStmt
+{
+       NodeTag         type;
+       char       *extname;
+       List       *options;            /* List of DefElem nodes */
+} CreateExtensionStmt;
+
 /* ----------------------
  *             Create/Drop FOREIGN DATA WRAPPER Statements
  * ----------------------
index 7dad00197eb411b3ad9e1d9a3c89db0233ea502d..4939b493bc2b471cbcf3ad1d1f2649b0295e6ff9 100644 (file)
@@ -150,6 +150,7 @@ PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD)
 PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD)
 PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD)
 PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD)
+PG_KEYWORD("extension", EXTENSION, UNRESERVED_KEYWORD)
 PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD)
 PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD)
 PG_KEYWORD("false", FALSE_P, RESERVED_KEYWORD)
index 540d16b844a9e85410a326f1148a3bdfa52bb397..d17eb67b1be55167c77e1f22bb9e2082e9a06982 100644 (file)
@@ -440,6 +440,8 @@ extern Datum pg_relation_filenode(PG_FUNCTION_ARGS);
 extern Datum pg_relation_filepath(PG_FUNCTION_ARGS);
 
 /* 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_read_file(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 */
 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 */
 extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
 
index 912578a84dc4da0d963f8ff8389d23c736f47248..655c5f9e3828a7cd75277a23c830a3f87256d6a2 100644 (file)
 #
 # Set one of these three variables to specify what is built:
 #
-#   MODULES -- list of shared objects to be built from source files with
-#     same stem (do not include suffix in this list)
-#   MODULE_big -- a shared object to build from multiple source files
+#   MODULES -- list of shared-library objects to be built from source files
+#     with same stem (do not include library suffixes in this list)
+#   MODULE_big -- a shared library to build from multiple source files
 #     (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:
 #
-#   MODULEDIR -- subdirectory into which DATA and DOCS files should be
-#     installed (if not set, default is "contrib")
+#   MODULEDIR -- subdirectory into which EXTENSION, DATA and DOCS files
+#     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_built -- random files to install into $PREFIX/share/$MODULEDIR,
 #     which need to be built first
@@ -82,7 +83,7 @@ ifdef PG_CPPFLAGS
 override CPPFLAGS := $(PG_CPPFLAGS) $(CPPFLAGS)
 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
 # shared library parameters
@@ -95,8 +96,8 @@ endif # MODULE_big
 
 
 install: all installdirs
-ifneq (,$(DATA)$(DATA_built))
-       @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \
+ifneq (,$(DATA)$(DATA_built)$(EXTENSION))
+       @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built) $(addsuffix .control, $(EXTENSION)); do \
          echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \
          $(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'; \
        done
@@ -167,8 +168,8 @@ endif # MODULE_big
 
 
 uninstall:
-ifneq (,$(DATA)$(DATA_built))
-       rm -f $(addprefix '$(DESTDIR)$(datadir)/$(datamoduledir)'/, $(notdir $(DATA) $(DATA_built)))
+ifneq (,$(DATA)$(DATA_built)$(EXTENSION))
+       rm -f $(addprefix '$(DESTDIR)$(datadir)/$(datamoduledir)'/, $(notdir $(DATA) $(DATA_built) $(addsuffix .control, $(EXTENSION))))
 endif
 ifneq (,$(DATA_TSEARCH))
        rm -f $(addprefix '$(DESTDIR)$(datadir)/tsearch_data'/, $(notdir $(DATA_TSEARCH)))
index 72e5630b1f10e65deb9ac9b6c59576da45b7fd71..8ddc116d665ebb6d0ea110a3ccf98876fe40c74e 100644 (file)
@@ -1279,6 +1279,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
           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);
+ 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_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"));
@@ -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))));
  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;
-(58 rows)
+(59 rows)
 
 SELECT tablename, rulename, definition FROM pg_rules
        ORDER BY tablename, rulename;
index d378e2641fabc6241d5e8948a6b84dd6f612ba47..59e1bdb8070340e9143300789536227fa73097d9 100644 (file)
@@ -101,6 +101,7 @@ SELECT relname, relhasindex
  pg_depend               | t
  pg_description          | t
  pg_enum                 | t
+ pg_extension            | t
  pg_foreign_data_wrapper | t
  pg_foreign_server       | t
  pg_foreign_table        | t