From d9572c4e3b474031060189050e14ef384b94e001 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 8 Feb 2011 16:08:41 -0500
Subject: [PATCH] Core support for "extensions", which are packages of SQL
 objects.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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
---
 doc/src/sgml/acronyms.sgml                 |    2 +-
 doc/src/sgml/catalogs.sgml                 |  195 +++
 doc/src/sgml/extend.sgml                   |  715 +++++++++-
 doc/src/sgml/ref/allfiles.sgml             |    3 +
 doc/src/sgml/ref/alter_extension.sgml      |   98 ++
 doc/src/sgml/ref/comment.sgml              |    1 +
 doc/src/sgml/ref/create_extension.sgml     |  118 ++
 doc/src/sgml/ref/drop_extension.sgml       |  121 ++
 doc/src/sgml/ref/psql-ref.sgml             |   14 +-
 doc/src/sgml/reference.sgml                |    3 +
 doc/src/sgml/release-9.0.sgml              |    5 +-
 doc/src/sgml/xfunc.sgml                    |  331 +----
 src/backend/catalog/Makefile               |    2 +-
 src/backend/catalog/dependency.c           |   45 +-
 src/backend/catalog/heap.c                 |    5 +-
 src/backend/catalog/objectaddress.c        |   14 +
 src/backend/catalog/pg_conversion.c        |    3 +
 src/backend/catalog/pg_depend.c            |   88 +-
 src/backend/catalog/pg_namespace.c         |   11 +-
 src/backend/catalog/pg_operator.c          |    5 +-
 src/backend/catalog/pg_proc.c              |    9 +-
 src/backend/catalog/pg_type.c              |   10 +-
 src/backend/catalog/system_views.sql       |    7 +
 src/backend/commands/Makefile              |    3 +-
 src/backend/commands/alter.c               |  196 ++-
 src/backend/commands/cluster.c             |    6 +-
 src/backend/commands/comment.c             |    6 +
 src/backend/commands/conversioncmds.c      |   31 +-
 src/backend/commands/extension.c           | 1401 ++++++++++++++++++++
 src/backend/commands/foreigncmds.c         |   27 +-
 src/backend/commands/functioncmds.c        |   42 +-
 src/backend/commands/opclasscmds.c         |   82 +-
 src/backend/commands/operatorcmds.c        |   28 +-
 src/backend/commands/proclang.c            |    9 +-
 src/backend/commands/tablecmds.c           |    1 +
 src/backend/commands/tsearchcmds.c         |  128 +-
 src/backend/commands/typecmds.c            |   23 +-
 src/backend/nodes/copyfuncs.c              |   14 +
 src/backend/nodes/equalfuncs.c             |   12 +
 src/backend/parser/gram.y                  |   56 +-
 src/backend/rewrite/rewriteDefine.c        |    2 +-
 src/backend/tcop/utility.c                 |   24 +
 src/backend/utils/adt/genfile.c            |   63 +-
 src/bin/pg_dump/common.c                   |    7 +
 src/bin/pg_dump/pg_dump.c                  |  283 +++-
 src/bin/pg_dump/pg_dump.h                  |   10 +
 src/bin/pg_dump/pg_dump_sort.c             |   65 +-
 src/bin/psql/command.c                     |    6 +
 src/bin/psql/describe.c                    |  164 ++-
 src/bin/psql/describe.h                    |    6 +
 src/bin/psql/help.c                        |    1 +
 src/bin/psql/tab-complete.c                |   36 +-
 src/include/catalog/catversion.h           |    2 +-
 src/include/catalog/dependency.h           |   16 +-
 src/include/catalog/indexing.h             |    6 +
 src/include/catalog/pg_extension.h         |   72 +
 src/include/catalog/pg_proc.h              |    6 +
 src/include/commands/alter.h               |   10 +-
 src/include/commands/conversioncmds.h      |    1 +
 src/include/commands/defrem.h              |    8 +
 src/include/commands/extension.h           |   40 +
 src/include/commands/typecmds.h            |    3 +-
 src/include/nodes/nodes.h                  |    1 +
 src/include/nodes/parsenodes.h             |   13 +
 src/include/parser/kwlist.h                |    1 +
 src/include/utils/builtins.h               |    6 +
 src/makefiles/pgxs.mk                      |   23 +-
 src/test/regress/expected/rules.out        |    3 +-
 src/test/regress/expected/sanity_check.out |    1 +
 69 files changed, 4204 insertions(+), 545 deletions(-)
 create mode 100644 doc/src/sgml/ref/alter_extension.sgml
 create mode 100644 doc/src/sgml/ref/create_extension.sgml
 create mode 100644 doc/src/sgml/ref/drop_extension.sgml
 create mode 100644 src/backend/commands/extension.c
 create mode 100644 src/include/catalog/pg_extension.h
 create mode 100644 src/include/commands/extension.h

diff --git a/doc/src/sgml/acronyms.sgml b/doc/src/sgml/acronyms.sgml
index d1ef489e366..8f6752f05d7 100644
--- a/doc/src/sgml/acronyms.sgml
+++ b/doc/src/sgml/acronyms.sgml
@@ -485,7 +485,7 @@
     <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>
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index f31662c2720..24aa22cbced 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -143,6 +143,11 @@
       <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>
@@ -2679,6 +2684,21 @@
      </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>
@@ -2848,6 +2868,101 @@
  </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>
 
@@ -6191,6 +6306,11 @@
     </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>
@@ -6286,6 +6406,81 @@
   </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>
 
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 2033ae21ba5..206eb6b9017 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -38,6 +38,11 @@
       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>
 
@@ -273,67 +278,701 @@
   &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>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index c44d11ef91b..ba85cae0837 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -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
index 00000000000..1b29d274cd6
--- /dev/null
+++ b/doc/src/sgml/ref/alter_extension.sgml
@@ -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>
diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index f1a1605df3c..e1fe0c16f9a 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -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
index 00000000000..961cab3839e
--- /dev/null
+++ b/doc/src/sgml/ref/create_extension.sgml
@@ -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
index 00000000000..1e09ec4c7a7
--- /dev/null
+++ b/doc/src/sgml/ref/drop_extension.sgml
@@ -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>
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index eacae71cdc7..cdf1abfa956 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -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>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 6ee8e5bcff8..47cd01f58e2 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -39,6 +39,7 @@
    &alterDatabase;
    &alterDefaultPrivileges;
    &alterDomain;
+   &alterExtension;
    &alterForeignDataWrapper;
    &alterForeignTable;
    &alterFunction;
@@ -78,6 +79,7 @@
    &createConversion;
    &createDatabase;
    &createDomain;
+   &createExtension;
    &createForeignDataWrapper;
    &createForeignTable;
    &createFunction;
@@ -114,6 +116,7 @@
    &dropConversion;
    &dropDatabase;
    &dropDomain;
+   &dropExtension;
    &dropForeignDataWrapper;
    &dropForeignTable;
    &dropFunction;
diff --git a/doc/src/sgml/release-9.0.sgml b/doc/src/sgml/release-9.0.sgml
index 2288f1b0e64..4902c058a96 100644
--- a/doc/src/sgml/release-9.0.sgml
+++ b/doc/src/sgml/release-9.0.sgml
@@ -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>
 
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 4ad50ec0cb5..4f2c23fab7a 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 7cffde17692..45aca8dd7f7 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -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 \
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index ba0ea178ac0..5c5f750a069 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -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,
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 14c69f3faa8..d9b272a7122 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -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;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c4608f7f173..82989acc088 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -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);
 	}
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index b2f66d75ac5..1ef6a9d24e2 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -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);
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 370051d91e3..b2ce148d625 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -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
  *
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
index c78aa019bff..172f99196cc 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -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);
 
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index c70483a7390..ccd0fe1997d 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -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);
 }
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 2ab87d2df5b..3f3877da286 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -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 */
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 8ceaab1fb12..9b574179ff9 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -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 */
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 4fa1453b14f..987026c8358 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -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
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 9d2a7322457..0aadbc56adb 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -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 \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 1c6ae0243e4..2c9340accf1 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -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;
 }
 
 
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 59a439413e1..4c4f356e790 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -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);
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 2e8c4df9272..bbb3f344093 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -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,
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index da024df5f0c..b5e4420ca8d 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -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
index 00000000000..50032031976
--- /dev/null
+++ b/src/backend/commands/extension.c
@@ -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);
+}
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 3a0ea9a6323..a2b5358e16f 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -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);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index dad65ee8ffa..3f25b3bf02a 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -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;
 }
 
 
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 662b9420387..68072dd4218 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -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;
 }
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 35bb76162d2..b4374a62f4f 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -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;
 }
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 3860105b266..b36f31ee6d5 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -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;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c0a4e6f954a..f67e9b9b162 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 7afd2f896ed..81f129dff6b 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -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;
 }
 
 /*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 25d0f3596e1..fb9d67a30a5 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -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;
 }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9b2c874d6d0..851186146dd 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 837eafaaccb..00d23ccfa56 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a1bcf02f5be..4c4536b9be3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 99c397b4f20..fecc4e27fa3 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -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
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index af2eba01d61..10a4438995f 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -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:
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index 93bc401c2d1..c3ec98aa5e2 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -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));
 }
 
 /*
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 55cc3126e1e..cc0db663200 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -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");
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 49c570016ad..dec96bc0253 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -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);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 43fd1ade27f..f0e9ae1e064 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -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);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index fe5cf56e695..f1c1c65e6cf 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -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)",
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 301dc11bcc2..a80678c2c3f 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -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;
 		}
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index d6c6cf1f15e..0342eb55bdc 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -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
  *
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 4e80bcf41f8..4b690b3b707 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -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 */
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 2647b1081fa..c44079e0343 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -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"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 84c68a7bff2..1c9623de1e5 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -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 ||
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 3defe0641f2..019cd8fab5a 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201102083
+#define CATALOG_VERSION_NO	201102084
 
 #endif
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index c6ab313edf8..4d7ff8853d1 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -50,6 +50,12 @@
  * 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);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 866942cf9a3..4118e645424 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -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
index 00000000000..0ad47b01a4b
--- /dev/null
+++ b/src/include/catalog/pg_extension.h
@@ -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 */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 836574355cd..9e6ec3dda50 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4873,6 +4873,12 @@ DESCR("record greater than or equal");
 DATA(insert OID = 2987 (  btrecordcmp	   PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "2249 2249" _null_ _null_ _null_ _null_ btrecordcmp _null_ _null_ _null_ ));
 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");
diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h
index 74d5132636c..21731685f5c 100644
--- a/src/include/commands/alter.h
+++ b/src/include/commands/alter.h
@@ -20,11 +20,11 @@
 
 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 */
diff --git a/src/include/commands/conversioncmds.h b/src/include/commands/conversioncmds.h
index 6156c4a94e6..f77023ffe32 100644
--- a/src/include/commands/conversioncmds.h
+++ b/src/include/commands/conversioncmds.h
@@ -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 */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 01f271bff43..157ee394614 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -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
index 00000000000..10d08935a00
--- /dev/null
+++ b/src/include/commands/extension.h
@@ -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 */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index b13363aaf7e..1b20296934f 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -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);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 63b9dd80be9..1ca5f1ef9a1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -355,6 +355,7 @@ typedef enum NodeTag
 	T_AlterTableSpaceOptionsStmt,
 	T_SecLabelStmt,
 	T_CreateForeignTableStmt,
+	T_CreateExtensionStmt,
 
 	/*
 	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b68fe10990a..5de4dbd5ec1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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
  * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 7dad00197eb..4939b493bc2 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -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)
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 540d16b844a..d17eb67b1be 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -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);
 
diff --git a/src/makefiles/pgxs.mk b/src/makefiles/pgxs.mk
index 912578a84dc..655c5f9e382 100644
--- a/src/makefiles/pgxs.mk
+++ b/src/makefiles/pgxs.mk
@@ -17,16 +17,17 @@
 #
 # 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)))
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 72e5630b1f1..8ddc116d665 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -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;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index d378e2641fa..59e1bdb8070 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -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
-- 
GitLab