SQL/MED catalog manipulation facilities
authorPeter Eisentraut <peter_e@gmx.net>
Fri, 19 Dec 2008 16:25:19 +0000 (16:25 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Fri, 19 Dec 2008 16:25:19 +0000 (16:25 +0000)
This doesn't do any remote or external things yet, but it gives modules
like plproxy and dblink a standardized and future-proof system for
managing their connection information.

Martin Pihlak and Peter Eisentraut

76 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/features.sgml
doc/src/sgml/func.sgml
doc/src/sgml/information_schema.sgml
doc/src/sgml/keywords.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/alter_foreign_data_wrapper.sgml [new file with mode: 0644]
doc/src/sgml/ref/alter_server.sgml [new file with mode: 0644]
doc/src/sgml/ref/alter_user_mapping.sgml [new file with mode: 0644]
doc/src/sgml/ref/create_foreign_data_wrapper.sgml [new file with mode: 0644]
doc/src/sgml/ref/create_server.sgml [new file with mode: 0644]
doc/src/sgml/ref/create_user_mapping.sgml [new file with mode: 0644]
doc/src/sgml/ref/drop_foreign_data_wrapper.sgml [new file with mode: 0644]
doc/src/sgml/ref/drop_server.sgml [new file with mode: 0644]
doc/src/sgml/ref/drop_user_mapping.sgml [new file with mode: 0644]
doc/src/sgml/ref/grant.sgml
doc/src/sgml/ref/psql-ref.sgml
doc/src/sgml/ref/revoke.sgml
doc/src/sgml/reference.sgml
src/Makefile
src/backend/Makefile
src/backend/catalog/Makefile
src/backend/catalog/aclchk.c
src/backend/catalog/dependency.c
src/backend/catalog/information_schema.sql
src/backend/catalog/sql_features.txt
src/backend/catalog/system_views.sql
src/backend/commands/Makefile
src/backend/commands/alter.c
src/backend/commands/foreigncmds.c [new file with mode: 0644]
src/backend/foreign/Makefile [new file with mode: 0644]
src/backend/foreign/dummy/Makefile [new file with mode: 0644]
src/backend/foreign/dummy/dummy_fdw.c [new file with mode: 0644]
src/backend/foreign/foreign.c [new file with mode: 0644]
src/backend/foreign/postgresql/Makefile [new file with mode: 0644]
src/backend/foreign/postgresql/postgresql_fdw.c [new file with mode: 0644]
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/makefuncs.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/tcop/utility.c
src/backend/utils/adt/acl.c
src/backend/utils/cache/syscache.c
src/bin/pg_dump/common.c
src/bin/pg_dump/dumputils.c
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dump_sort.c
src/bin/psql/command.c
src/bin/psql/describe.c
src/bin/psql/describe.h
src/bin/psql/help.c
src/bin/psql/tab-complete.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/catalog/indexing.h
src/include/catalog/pg_foreign_data_wrapper.h [new file with mode: 0644]
src/include/catalog/pg_foreign_server.h [new file with mode: 0644]
src/include/catalog/pg_proc.h
src/include/catalog/pg_user_mapping.h [new file with mode: 0644]
src/include/commands/defrem.h
src/include/foreign/foreign.h [new file with mode: 0644]
src/include/nodes/makefuncs.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/utils/acl.h
src/include/utils/builtins.h
src/include/utils/syscache.h
src/test/regress/expected/foreign_data.out [new file with mode: 0644]
src/test/regress/expected/rules.out
src/test/regress/expected/sanity_check.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/foreign_data.sql [new file with mode: 0644]

index f5f26d035d9088b3fbd639dba9c0f5e9e50a1bb2..52be7a4d26dcd13910a9f3296681862162b66d8c 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.184 2008/12/18 18:20:33 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.185 2008/12/19 16:25:16 petere Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
       <entry>enum label and value definitions</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>
+     </row>
+
+     <row>
+      <entry><link linkend="catalog-pg-foreign-server"><structname>pg_foreign_server</structname></link></entry>
+      <entry>foreign server definitions</entry>
+     </row>
+
      <row>
       <entry><link linkend="catalog-pg-index"><structname>pg_index</structname></link></entry>
       <entry>additional index information</entry>
       <entry><link linkend="catalog-pg-type"><structname>pg_type</structname></link></entry>
       <entry>data types</entry>
      </row>
+
+     <row>
+      <entry><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link></entry>
+      <entry>mappings of users to foreign servers</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
  </sect1>
 
 
+ <sect1 id="catalog-pg-foreign-data-wrapper">
+  <title><structname>pg_foreign_data_wrapper</structname></title>
+
+  <indexterm zone="catalog-pg-foreign-data-wrapper">
+   <primary>pg_foreign_data_wrapper</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_foreign_data_wrapper</structname> stores
+   foreign-data wrapper definitions.  A foreign-data wrapper is the
+   mechanism by which external data, residing on foreign servers, is
+   accessed.
+  </para>
+
+  <table>
+   <title><structname>pg_foreign_data_wrapper</> 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>fdwname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the foreign-data wrapper</entry>
+     </row>
+
+     <row>
+      <entry><structfield>fdwowner</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 foreign-data wrapper</entry>
+     </row>
+
+     <row>
+      <entry><structfield>fdwlibrary</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry></entry>
+      <entry>File name of the library implementing this foreign-data wrapper</entry>
+     </row>
+
+     <row>
+      <entry><structfield>fdwacl</structfield></entry>
+      <entry><type>aclitem[]</type></entry>
+      <entry></entry>
+      <entry>
+       Access privileges; see
+       <xref linkend="sql-grant" endterm="sql-grant-title"> and
+       <xref linkend="sql-revoke" endterm="sql-revoke-title">
+       for details
+      </entry>
+     </row>
+
+     <row>
+      <entry><structfield>fdwoptions</structfield></entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>
+       Foreign-data wrapper specific options, as <quote>keyword=value</> strings
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+
+ <sect1 id="catalog-pg-foreign-server">
+  <title><structname>pg_foreign_server</structname></title>
+
+  <indexterm zone="catalog-pg-foreign-server">
+   <primary>pg_foreign_server</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_foreign_server</structname> stores
+   foreign server definitions.  A foreign server describes the
+   connection to a remote server, managing external data.  Foreign
+   servers are accessed via foreign-data wrappers.
+  </para>
+
+  <table>
+   <title><structname>pg_foreign_server</> 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>srvname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the foreign server</entry>
+     </row>
+
+     <row>
+      <entry><structfield>srvowner</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 foreign server</entry>
+     </row>
+
+     <row>
+      <entry><structfield>srvfdw</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-foreign-data-wrapper"><structname>pg_foreign_data_wrapper</structname></link>.oid</literal></entry>
+      <entry>The OID of the foreign-data wrapper of this foreign server</entry>
+     </row>
+
+     <row>
+      <entry><structfield>srvtype</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry></entry>
+      <entry>Type of the server (optional)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>srvversion</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry></entry>
+      <entry>Version of the server (optional)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>srvacl</structfield></entry>
+      <entry><type>aclitem[]</type></entry>
+      <entry></entry>
+      <entry>
+       Access privileges; see
+       <xref linkend="sql-grant" endterm="sql-grant-title"> and
+       <xref linkend="sql-revoke" endterm="sql-revoke-title">
+       for details
+      </entry>
+     </row>
+
+     <row>
+      <entry><structfield>srvoptions</structfield></entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>
+       Foreign server specific options, as <quote>keyword=value</> strings.
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+
+ <sect1 id="catalog-pg-user-mapping">
+  <title><structname>pg_user_mapping</structname></title>
+
+  <indexterm zone="catalog-pg-user-mapping">
+   <primary>pg_user_mapping</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_user_mapping</structname> stores
+   the mappings from local user to remote.  Access to this catalog is
+   restricted from normal users, use the view
+   <link linkend="view-pg-user-mappings"><structname>pg_user_mappings</structname></link>
+   instead.
+  </para>
+
+  <table>
+   <title><structname>pg_user_mapping</> 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>umuser</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+      <entry>OID of the local role being mapped, 0 if the user mapping is public</entry>
+     </row>
+
+     <row>
+      <entry><structfield>umserver</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-foreign-server"><structname>pg_foreign_server</structname></link>.oid</literal></entry>
+      <entry>
+       The OID of the foreign server that contains this mapping
+      </entry>
+     </row>
+
+     <row>
+      <entry><structfield>umoptions</structfield></entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>
+       User mapping specific options, as <quote>keyword=value</> strings.
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+
  <sect1 id="catalog-pg-index">
   <title><structname>pg_index</structname></title>
 
 
  </sect1>
 
+ <sect1 id="view-pg-user-mappings">
+  <title><structname>pg_user_mappings</structname></title>
+
+  <indexterm zone="view-pg-user-mappings">
+   <primary>pg_user_mappings</primary>
+  </indexterm>
+
+  <para>
+   The view <structname>pg_user_mappings</structname> provides access
+   to information about user mappings.  This is essentially a publicly
+   readable view of
+   <link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link>
+   that leaves out the options field if the user has no rights to use
+   it.
+  </para>
+
+  <table>
+   <title><structname>pg_user_mappings</> Columns</title>
+
+   <tgroup cols=3>
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>umid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-user-mapping"><structname>pg_user_mapping</structname></link>.oid</literal></entry>
+      <entry>OID of the user mapping</entry>
+     </row>
+
+     <row>
+      <entry><structfield>srvid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-foreign-server"><structname>pg_foreign_server</structname></link>.oid</literal></entry>
+      <entry>
+       The OID of the foreign server that contains this mapping
+      </entry>
+     </row>
+
+     <row>
+      <entry><structfield>srvname</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry></entry>
+      <entry>
+       Name of the foreign server
+      </entry>
+     </row>
+
+     <row>
+      <entry><structfield>umuser</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+      <entry>OID of the local role being mapped, 0 if the user mapping is public</entry>
+     </row>
+
+     <row>
+      <entry><structfield>usename</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Name of the local user to be mapped</entry>
+     </row>
+
+     <row>
+      <entry><structfield>umoptions</structfield></entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>
+       User mapping specific options, as <quote>keyword=value</>
+       strings, if the current user is the owner of the foreign
+       server, else null.
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+
  <sect1 id="view-pg-views">
   <title><structname>pg_views</structname></title>
 
index 712ad4e4ecf207578f72b48ccdd062eabdebc042..56b5be3318307307a29834fcc5ffa069283201d8 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/features.sgml,v 2.29 2008/11/27 12:12:02 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/features.sgml,v 2.30 2008/12/19 16:25:16 petere Exp $ -->
 
 <appendix id="features">
  <title>SQL Conformance</title>
  </para>
 
  <para>
-  The <productname>PostgreSQL</productname> core covers parts 1, 2,
+  The <productname>PostgreSQL</productname> core covers parts 1, 2, 9,
   11, and 14.  Part 3 is covered by the ODBC driver, and part 13 is
   covered by the PL/Java plug-in, but exact conformance is currently
   not being verified for these components.  There are currently no
-  implementations of parts 4, 9, and 10
+  implementations of parts 4 and 10
   for <productname>PostgreSQL</productname>.
  </para>
 
index c08c4801882c5929581c8e1128dba969bbc9edd2..de50c0e1d5691168ee639a176f127df764611fc8 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.462 2008/12/18 18:20:33 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.463 2008/12/19 16:25:16 petere Exp $ -->
 
  <chapter id="functions">
   <title>Functions and Operators</title>
@@ -11335,6 +11335,21 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
        <entry><type>boolean</type></entry>
        <entry>does current user have privilege for database</entry>
       </row>
+      <row>
+       <entry><literal><function>has_foreign_data_wrapper_privilege</function>(<parameter>user</parameter>,
+                                  <parameter>fdw</parameter>,
+                                  <parameter>privilege</parameter>)</literal>
+       </entry>
+       <entry><type>boolean</type></entry>
+       <entry>does user have privilege for foreign-data wrapper</entry>
+      </row>
+      <row>
+       <entry><literal><function>has_foreign_data_wrapper_privilege</function>(<parameter>fdw</parameter>,
+                                  <parameter>privilege</parameter>)</literal>
+       </entry>
+       <entry><type>boolean</type></entry>
+       <entry>does current user have privilege for foreign-data wrapper</entry>
+      </row>
       <row>
        <entry><literal><function>has_function_privilege</function>(<parameter>user</parameter>,
                                   <parameter>function</parameter>,
@@ -11380,6 +11395,21 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
        <entry><type>boolean</type></entry>
        <entry>does current user have privilege for schema</entry>
       </row>
+      <row>
+       <entry><literal><function>has_server_privilege</function>(<parameter>user</parameter>,
+                                  <parameter>server</parameter>,
+                                  <parameter>privilege</parameter>)</literal>
+       </entry>
+       <entry><type>boolean</type></entry>
+       <entry>does user have privilege for foreign server</entry>
+      </row>
+      <row>
+       <entry><literal><function>has_server_privilege</function>(<parameter>server</parameter>,
+                                  <parameter>privilege</parameter>)</literal>
+       </entry>
+       <entry><type>boolean</type></entry>
+       <entry>does current user have privilege for foreign server</entry>
+      </row>
       <row>
        <entry><literal><function>has_table_privilege</function>(<parameter>user</parameter>,
                                   <parameter>table</parameter>,
@@ -11435,12 +11465,18 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
    <indexterm>
     <primary>has_function_privilege</primary>
    </indexterm>
+   <indexterm>
+    <primary>has_foreign_data_wrapper_privilege</primary>
+   </indexterm>
    <indexterm>
     <primary>has_language_privilege</primary>
    </indexterm>
    <indexterm>
     <primary>has_schema_privilege</primary>
    </indexterm>
+   <indexterm>
+    <primary>has_server_privilege</primary>
+   </indexterm>
    <indexterm>
     <primary>has_table_privilege</primary>
    </indexterm>
@@ -11478,6 +11514,14 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
 </programlisting>
    </para>
 
+   <para>
+    <function>has_foreign_data_wrapper_privilege</function> checks whether a user
+    can access a foreign-data wrapper in a particular way.  The possibilities for its
+    arguments are analogous to <function>has_table_privilege</function>.
+    The desired access privilege type must evaluate to
+    <literal>USAGE</literal>.
+   </para>
+
    <para>
     <function>has_language_privilege</function> checks whether a user
     can access a procedural language in a particular way.  The possibilities
@@ -11495,6 +11539,14 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
     <literal>USAGE</literal>.
    </para>
 
+   <para>
+    <function>has_server_privilege</function> checks whether a user
+    can access a foreign server in a particular way.  The possibilities for its
+    arguments are analogous to <function>has_table_privilege</function>.
+    The desired access privilege type must evaluate to
+    <literal>USAGE</literal>.
+   </para>
+
    <para>
     <function>has_table_privilege</function> checks whether a user
     can access a table in a particular way.  The user can be
index da01c9cc5a71085eaf2dbb8ce85e251543393465..f645c1252d625d680c409e86bff09f40f9e2fad3 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/information_schema.sgml,v 1.35 2008/11/25 20:47:42 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/information_schema.sgml,v 1.36 2008/12/19 16:25:16 petere Exp $ -->
 
 <chapter id="information-schema">
  <title>The Information Schema</title>
@@ -2145,6 +2145,237 @@ ORDER BY c.ordinal_position;
   </table>
  </sect1>
 
+ <sect1 id="infoschema-foreign-data-wrapper-options">
+  <title><literal>foreign_data_wrapper_options</literal></title>
+
+  <para>
+   The view <literal>foreign_data_wrapper_options</literal> contains
+   all the options defined for foreign-data wrappers in the current
+   database.  Only those foreign-data wrappers are shown that the
+   current user has access to (by way of being the owner or having
+   some privilege).
+  </para>
+
+  <table>
+   <title><literal>foreign_data_wrapper_options</literal> Columns</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Data Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><literal>foreign_data_wrapper_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that the foreign-data wrapper is defined in (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_data_wrapper_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign-data wrapper</entry>
+     </row>
+
+     <row>
+      <entry><literal>option_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of an option</entry>
+     </row>
+
+     <row>
+      <entry><literal>option_value</literal></entry>
+      <entry><type>character_data</type></entry>
+      <entry>Value of the option</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+ <sect1 id="infoschema-foreign-data-wrappers">
+  <title><literal>foreign_data_wrappers</literal></title>
+
+  <para>
+   The view <literal>foreign_data_wrappers</literal> contains all
+   foreign-data wrappers defined in the current database.  Only those
+   foreign-data wrappers are shown that the current user has access to
+   (by way of being the owner or having some privilege).
+  </para>
+
+  <table>
+   <title><literal>foreign_data_wrappers</literal> Columns</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Data Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><literal>foreign_data_wrapper_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that contains the foreign-data
+      wrapper (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_data_wrapper_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign-data wrapper</entry>
+     </row>
+
+     <row>
+      <entry><literal>authorization_identifier</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the owner of the foreign server</entry>
+     </row>
+
+     <row>
+      <entry><literal>library_name</literal></entry>
+      <entry><type>character_data</type></entry>
+      <entry>File name of the library that implementing this foreign-data wrapper</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_data_wrapper_language</literal></entry>
+      <entry><type>character_data</type></entry>
+      <entry>Language used to implement this foreign-data wrapper</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+ <sect1 id="infoschema-foreign-server-options">
+  <title><literal>foreign_server_options</literal></title>
+
+  <para>
+   The view <literal>foreign_server_options</literal> contains all the
+   options defined for foreign servers in the current database.  Only
+   those foreign servers are shown that the current user has access to
+   (by way of being the owner or having some privilege).
+  </para>
+
+  <table>
+   <title><literal>foreign_server_options</literal> Columns</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Data Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><literal>foreign_server_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that the foreign server is defined in (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign server</entry>
+     </row>
+
+     <row>
+      <entry><literal>option_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of an option</entry>
+     </row>
+
+     <row>
+      <entry><literal>option_value</literal></entry>
+      <entry><type>character_data</type></entry>
+      <entry>Value of the option</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+ <sect1 id="infoschema-foreign-servers">
+  <title><literal>foreign_servers</literal></title>
+
+  <para>
+   The view <literal>foreign_servers</literal> contains all foreign
+   servers defined in the current database.  Only those foreign
+   servers are shown that the current user has access to (by way of
+   being the owner or having some privilege).
+  </para>
+
+  <table>
+   <title><literal>foreign_servers</literal> Columns</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Data Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><literal>foreign_server_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that the foreign server is defined in (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign server</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_data_wrapper_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that contains the foreign-data
+      wrapper used by the foreign server (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_data_wrapper_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign-data wrapper used by the foreign server</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_type</literal></entry>
+      <entry><type>character_data</type></entry>
+      <entry>Foreign server type information, if specified upon creation</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_version</literal></entry>
+      <entry><type>character_data</type></entry>
+      <entry>Foreign server version information, if specified upon creation</entry>
+     </row>
+
+     <row>
+      <entry><literal>authorization_identifier</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the owner of the foreign server</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
  <sect1 id="infoschema-key-column-usage">
   <title><literal>key_column_usage</literal></title>
 
@@ -2848,15 +3079,11 @@ ORDER BY c.ordinal_position;
   <title><literal>role_usage_grants</literal></title>
 
   <para>
-   The view <literal>role_usage_grants</literal> is meant to identify
+   The view <literal>role_usage_grants</literal> identifies
    <literal>USAGE</literal> privileges granted on various kinds of
-   objects to a currently enabled role or by a currently enabled role.
-   In <productname>PostgreSQL</productname>, this currently only
-   applies to domains, and since domains do not have real privileges
-   in <productname>PostgreSQL</productname>, this view is empty.
+   objects where the grantor or grantee is a currently enabled role.
    Further information can be found under
-   <literal>usage_privileges</literal>.  In the future, this view might
-   contain more useful information.
+   <literal>usage_privileges</literal>.
   </para>
 
   <table>
@@ -2875,13 +3102,13 @@ ORDER BY c.ordinal_position;
      <row>
       <entry><literal>grantor</literal></entry>
       <entry><type>sql_identifier</type></entry>
-      <entry>In the future, the name of the role that granted the privilege</entry>
+      <entry>The name of the role that granted the privilege</entry>
      </row>
 
      <row>
       <entry><literal>grantee</literal></entry>
       <entry><type>sql_identifier</type></entry>
-      <entry>In the future, the name of the role that the privilege was granted to</entry>
+      <entry>The name of the role that the privilege was granted to</entry>
      </row>
 
      <row>
@@ -2893,7 +3120,8 @@ ORDER BY c.ordinal_position;
      <row>
       <entry><literal>object_schema</literal></entry>
       <entry><type>sql_identifier</type></entry>
-      <entry>Name of the schema containing the object</entry>
+      <entry>Name of the schema containing the object, if applicable,
+      else an empty string</entry>
      </row>
 
      <row>
@@ -2905,7 +3133,7 @@ ORDER BY c.ordinal_position;
      <row>
       <entry><literal>object_type</literal></entry>
       <entry><type>character_data</type></entry>
-      <entry>In the future, the type of the object</entry>
+      <entry><literal>DOMAIN</literal> or <literal>FOREIGN DATA WRAPPER</literal> or <literal>FOREIGN SERVER</literal></entry>
      </row>
 
      <row>
@@ -4718,15 +4946,20 @@ ORDER BY c.ordinal_position;
   <title><literal>usage_privileges</literal></title>
 
   <para>
-   The view <literal>usage_privileges</literal> is meant to identify
+   The view <literal>usage_privileges</literal> identifies
    <literal>USAGE</literal> privileges granted on various kinds of
    objects to a currently enabled role or by a currently enabled role.
-   In <productname>PostgreSQL</productname>, this currently only
-   applies to domains, and since domains do not have real privileges
+   In <productname>PostgreSQL</productname>, this currently applies to
+   domains, foreign-data wrappers, and foreign servers.  There is one
+   row for each combination of object, grantor, and grantee.
+  </para>
+
+  <para>
+   Since domains do not have real privileges
    in <productname>PostgreSQL</productname>, this view shows implicit
-   <literal>USAGE</literal> privileges granted to
-   <literal>PUBLIC</literal> for all domains.  In the future, this
-   view might contain more useful information.
+   non-grantable <literal>USAGE</literal> privileges granted by the
+   owner to <literal>PUBLIC</literal> for all domains.  The other
+   object types, however, show real privileges.
   </para>
 
   <table>
@@ -4745,13 +4978,13 @@ ORDER BY c.ordinal_position;
      <row>
       <entry><literal>grantor</literal></entry>
       <entry><type>sql_identifier</type></entry>
-      <entry>Currently set to the name of the owner of the object</entry>
+      <entry>Name of the role that granted the privilege</entry>
      </row>
 
      <row>
       <entry><literal>grantee</literal></entry>
       <entry><type>sql_identifier</type></entry>
-      <entry>Currently always <literal>PUBLIC</literal></entry>
+      <entry>Name of the role that the privilege was granted to</entry>
      </row>
 
      <row>
@@ -4763,7 +4996,8 @@ ORDER BY c.ordinal_position;
      <row>
       <entry><literal>object_schema</literal></entry>
       <entry><type>sql_identifier</type></entry>
-      <entry>Name of the schema containing the object</entry>
+      <entry>Name of the schema containing the object, if applicable,
+      else an empty string</entry>
      </row>
 
      <row>
@@ -4775,7 +5009,7 @@ ORDER BY c.ordinal_position;
      <row>
       <entry><literal>object_type</literal></entry>
       <entry><type>character_data</type></entry>
-      <entry>Currently always <literal>DOMAIN</literal></entry>
+      <entry><literal>DOMAIN</literal> or <literal>FOREIGN DATA WRAPPER</literal> or <literal>FOREIGN SERVER</literal></entry>
      </row>
 
      <row>
@@ -4787,7 +5021,115 @@ ORDER BY c.ordinal_position;
      <row>
       <entry><literal>is_grantable</literal></entry>
       <entry><type>character_data</type></entry>
-      <entry>Currently always <literal>NO</literal></entry>
+      <entry><literal>YES</literal> if the privilege is grantable, <literal>NO</literal> if not</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+ <sect1 id="infoschema-user-mapping-options">
+  <title><literal>user_mapping_options</literal></title>
+
+  <para>
+   The view <literal>user_mapping_options</literal> contains all the
+   options defined for user mappings in the current database.  Only
+   those user mappings are shown where the current user has access to
+   the corresponding foreign server (by way of being the owner or
+   having some privilege).
+  </para>
+
+  <table>
+   <title><literal>user_mapping_options</literal> Columns</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Data Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><literal>authorization_identifier</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the user being mapped,
+      or <literal>PUBLIC</literal> if the mapping is public</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that the foreign server used by this
+      mapping is defined in (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign server used by this mapping</entry>
+     </row>
+
+     <row>
+      <entry><literal>option_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of an option</entry>
+     </row>
+
+     <row>
+      <entry><literal>option_value</literal></entry>
+      <entry><type>character_data</type></entry>
+      <entry>Value of the option</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+ <sect1 id="infoschema-user-mappings">
+  <title><literal>user_mappings</literal></title>
+
+  <para>
+   The view <literal>user_mappings</literal> contains all user
+   mappings defined in the current database.  Only those user mappings
+   are shown where the current user has access to the corresponding
+   foreign server (by way of being the owner or having some
+   privilege).
+  </para>
+
+  <table>
+   <title><literal>user_mappings</literal> Columns</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Data Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><literal>authorization_identifier</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the user being mapped,
+      or <literal>PUBLIC</literal> if the mapping is public</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that the foreign server used by this
+      mapping is defined in (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign server used by this mapping</entry>
      </row>
     </tbody>
    </tgroup>
index de3bedfdcbe94aaf9fd8569211c141c14d52785e..f7f7d649563162c7e1a04b7d8e3cdb63daa66811 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/keywords.sgml,v 2.21 2008/12/03 12:39:57 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/keywords.sgml,v 2.22 2008/12/19 16:25:16 petere Exp $ -->
 
 <appendix id="sql-keywords-appendix">
  <title><acronym>SQL</acronym> Key Words</title>
     <entry>reserved</entry>
     <entry>reserved</entry>
    </row>
+   <row>
+    <entry><token>LIBRARY</token></entry>
+    <entry>non-reserved</entry>
+    <entry>non-reserved</entry>
+    <entry>non-reserved</entry>
+    <entry></entry>
+    <entry></entry>
+   </row>
    <row>
     <entry><token>LIKE</token></entry>
     <entry>reserved (can be function or type)</entry>
    <row>
     <entry><token>MAPPING</token></entry>
     <entry>non-reserved</entry>
-    <entry></entry>
-    <entry></entry>
+    <entry>non-reserved</entry>
+    <entry>non-reserved</entry>
     <entry></entry>
     <entry></entry>
    </row>
    </row>
    <row>
     <entry><token>OPTIONS</token></entry>
-    <entry></entry>
+    <entry>non-reserved</entry>
     <entry>non-reserved</entry>
     <entry>non-reserved</entry>
     <entry>non-reserved</entry>
     <entry>non-reserved</entry>
     <entry>non-reserved</entry>
    </row>
+   <row>
+    <entry><token>SERVER</token></entry>
+    <entry>non-reserved</entry>
+    <entry>non-reserved</entry>
+    <entry>non-reserved</entry>
+    <entry></entry>
+    <entry></entry>
+   </row>
    <row>
     <entry><token>SERVER_NAME</token></entry>
     <entry></entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
    </row>
+   <row>
+    <entry><token>WRAPPER</token></entry>
+    <entry>non-reserved</entry>
+    <entry>non-reserved</entry>
+    <entry>non-reserved</entry>
+    <entry></entry>
+    <entry></entry>
+   </row>
    <row>
     <entry><token>WRITE</token></entry>
     <entry>non-reserved</entry>
index b80a2cf8f04895372b5b20c54490f49b72bac88f..6c20b623c49ad8f866edab0b3256de0b736fd639 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.73 2008/03/27 17:24:16 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.74 2008/12/19 16:25:16 petere Exp $
 PostgreSQL documentation
 Complete list of usable sgml source files in this directory.
 -->
@@ -10,6 +10,7 @@ Complete list of usable sgml source files in this directory.
 <!entity alterConversion    system "alter_conversion.sgml">
 <!entity alterDatabase      system "alter_database.sgml">
 <!entity alterDomain        system "alter_domain.sgml">
+<!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml">
 <!entity alterFunction      system "alter_function.sgml">
 <!entity alterGroup         system "alter_group.sgml">
 <!entity alterIndex         system "alter_index.sgml">
@@ -19,6 +20,7 @@ Complete list of usable sgml source files in this directory.
 <!entity alterOperatorFamily system "alter_opfamily.sgml">
 <!entity alterRole          system "alter_role.sgml">
 <!entity alterSchema        system "alter_schema.sgml">
+<!entity alterServer        system "alter_server.sgml">
 <!entity alterSequence      system "alter_sequence.sgml">
 <!entity alterTable         system "alter_table.sgml">
 <!entity alterTableSpace    system "alter_tablespace.sgml">
@@ -29,6 +31,7 @@ Complete list of usable sgml source files in this directory.
 <!entity alterTrigger       system "alter_trigger.sgml">
 <!entity alterType          system "alter_type.sgml">
 <!entity alterUser          system "alter_user.sgml">
+<!entity alterUserMapping   system "alter_user_mapping.sgml">
 <!entity alterView          system "alter_view.sgml">
 <!entity analyze            system "analyze.sgml">
 <!entity begin              system "begin.sgml">
@@ -45,6 +48,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 createForeignDataWrapper system "create_foreign_data_wrapper.sgml">
 <!entity createFunction     system "create_function.sgml">
 <!entity createGroup        system "create_group.sgml">
 <!entity createIndex        system "create_index.sgml">
@@ -56,6 +60,7 @@ Complete list of usable sgml source files in this directory.
 <!entity createRule         system "create_rule.sgml">
 <!entity createSchema       system "create_schema.sgml">
 <!entity createSequence     system "create_sequence.sgml">
+<!entity createServer       system "create_server.sgml">
 <!entity createTable        system "create_table.sgml">
 <!entity createTableAs      system "create_table_as.sgml">
 <!entity createTableSpace   system "create_tablespace.sgml">
@@ -66,6 +71,7 @@ Complete list of usable sgml source files in this directory.
 <!entity createTSTemplate   system "create_tstemplate.sgml">
 <!entity createType         system "create_type.sgml">
 <!entity createUser         system "create_user.sgml">
+<!entity createUserMapping  system "create_user_mapping.sgml">
 <!entity createView         system "create_view.sgml">
 <!entity deallocate         system "deallocate.sgml">
 <!entity declare            system "declare.sgml">
@@ -76,6 +82,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 dropForeignDataWrapper system "drop_foreign_data_wrapper.sgml">
 <!entity dropFunction       system "drop_function.sgml">
 <!entity dropGroup          system "drop_group.sgml">
 <!entity dropIndex          system "drop_index.sgml">
@@ -88,6 +95,7 @@ Complete list of usable sgml source files in this directory.
 <!entity dropRule           system "drop_rule.sgml">
 <!entity dropSchema         system "drop_schema.sgml">
 <!entity dropSequence       system "drop_sequence.sgml">
+<!entity dropServer         system "drop_server.sgml">
 <!entity dropTable          system "drop_table.sgml">
 <!entity dropTableSpace     system "drop_tablespace.sgml">
 <!entity dropTrigger        system "drop_trigger.sgml">
@@ -97,6 +105,7 @@ Complete list of usable sgml source files in this directory.
 <!entity dropTSTemplate     system "drop_tstemplate.sgml">
 <!entity dropType           system "drop_type.sgml">
 <!entity dropUser           system "drop_user.sgml">
+<!entity dropUserMapping    system "drop_user_mapping.sgml">
 <!entity dropView           system "drop_view.sgml">
 <!entity end                system "end.sgml">
 <!entity execute            system "execute.sgml">
diff --git a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
new file mode 100644 (file)
index 0000000..877c475
--- /dev/null
@@ -0,0 +1,132 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERFOREIGNDATAWRAPPER">
+ <refmeta>
+  <refentrytitle id="sql-alterforeigndatawrapper-title">ALTER FOREIGN DATA WRAPPER</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER FOREIGN DATA WRAPPER</refname>
+  <refpurpose>change the definition of a foreign-data wrapper</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterforeigndatawrapper">
+  <primary>ALTER FOREIGN DATA WRAPPER</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
+    [ LIBRARY '<replaceable class="parameter">libraryname</replaceable>' ]
+    [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ]) ]
+ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWNER TO <replaceable>new_owner</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER FOREIGN DATA WRAPPER</command> changes the
+   definition of a foreign-data wrapper.  The first form of the
+   command changes the library or the generic options of the
+   foreign-data wrapper (at least one clause is required).  The second
+   form changes the owner of the foreign-data wrapper.
+  </para>
+
+  <para>
+   Only superusers can alter foreign-data wrappers.  Additionally,
+   only superusers can own foreign-data wrappers.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing foreign-data wrapper.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">libraryname</replaceable></term>
+    <listitem>
+     <para>
+      New name of the foreign-data wrapper library.
+     </para>
+
+     <para>
+      Note that it is possible that after changing the library, the
+      options to the foreign-data wrapper, servers, and user mappings
+      have become invalid.   It is up to the user to make sure that
+      these options are correct before using the foreign-data
+      wrapper.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      Change options for the foreign-data
+      wrapper.  <literal>ADD</>, <literal>SET</>, and <literal>DROP</>
+      specify the action to be performed.  <literal>ADD</> is assumed
+      if no operation is explicitly specified.  Option names must be
+      unique; names and values are also validated using the foreign
+      data wrapper library.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Change a foreign-data wrapper <literal>dbi</>, add
+   option <literal>foo</>, drop <literal>bar</>:
+<programlisting>
+ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo '1', DROP 'bar');
+</programlisting>
+  </para>
+
+  <para>
+   Change the foreign-data wrapper <literal>dbi</> library
+   to <literal>/home/bob/mylibrary.so</>:
+<programlisting>
+ALTER FOREIGN DATA WRAPPER dbi LIBRARY '/home/bob/mylibrary.so';
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>ALTER FOREIGN DATA WRAPPER</command> conforms to ISO/IEC
+   9075-9 (SQL/MED).  The standard does not specify the <literal>OWNER
+   TO</> variant of the command.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createforeigndatawrapper" endterm="sql-createforeigndatawrapper-title"></member>
+   <member><xref linkend="sql-dropforeigndatawrapper" endterm="sql-dropforeigndatawrapper-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/alter_server.sgml b/doc/src/sgml/ref/alter_server.sgml
new file mode 100644 (file)
index 0000000..0595bca
--- /dev/null
@@ -0,0 +1,123 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_server.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERSERVER">
+ <refmeta>
+  <refentrytitle id="sql-alterserver-title">ALTER SERVER</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER SERVER</refname>
+  <refpurpose>change the definition of a foreign server</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterserver">
+  <primary>ALTER SERVER</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER SERVER <replaceable class="parameter">servername</replaceable> [ VERSION 'newversion' ]
+    [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] ) ]
+ALTER SERVER <replaceable class="PARAMETER">servername</replaceable> OWNER TO <replaceable>new_owner</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER SERVER</command> changes the definition of a foreign
+   server.  The first form changes the server version string or the
+   generic options of the server (at least one clause is required).
+   The second form changes the owner of the server.
+  </para>
+
+  <para>
+   To alter the server you must be the owner of the server.
+   Additionally to alter the owner, you must own the server and also
+   be a direct or indirect member of the new owning role, and you must
+   have <literal>USAGE</> privilege on the server's foreign-data
+   wrapper.  (Note that superusers satisfy all these criteria
+   automatically.)
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">servername</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing server.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">serverversion</replaceable></term>
+    <listitem>
+     <para>
+      New server version.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      Change options for the
+      server.  <literal>ADD</>, <literal>SET</>, and <literal>DROP</>
+      specify the action to be performed.  <literal>ADD</> is assumed
+      if no operation is explicitly specified.  Option names must be
+      unique; names and values are also validated using the server's
+      foreign-data wrapper library.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Alter server <literal>foo</>, add connection options:
+<programlisting>
+ALTER SERVER foo OPTIONS (host 'foo', dbname 'foodb');
+</programlisting>
+  </para>
+
+  <para>
+   Alter server <literal>foo</>, change version,
+   change <literal>host</> option:
+<programlisting>
+ALTER SERVER foo VERSION '8.4' OPTIONS (SET host 'baz');
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>ALTER SERVER</command> conforms to ISO/IEC 9075-9 (SQL/MED).
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createserver" endterm="sql-createserver-title"></member>
+   <member><xref linkend="sql-dropserver" endterm="sql-dropserver-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/alter_user_mapping.sgml b/doc/src/sgml/ref/alter_user_mapping.sgml
new file mode 100644 (file)
index 0000000..38bff39
--- /dev/null
@@ -0,0 +1,119 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_user_mapping.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERUSERMAPPING">
+ <refmeta>
+  <refentrytitle id="sql-alterusermapping-title">ALTER USER MAPPING</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER USER MAPPING</refname>
+  <refpurpose>change the definition of a user mapping</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterusermapping">
+  <primary>ALTER USER MAPPING</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER USER MAPPING FOR { <replaceable class="parameter">username</replaceable> | USER | CURRENT_USER | PUBLIC }
+    SERVER <replaceable class="parameter">servername</replaceable>
+    OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER USER MAPPING</command> changes the definition of a
+   user mapping.  Only the owner of the server can change the user
+   mappings of that server.
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">username</replaceable></term>
+    <listitem>
+     <para>
+      User name of the mapping. <literal>CURRENT_USER</>
+      and <literal>USER</> match the name of the current
+      user. <literal>PUBLIC</> is used to match all present and future
+      user names in the system.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">servername</replaceable></term>
+    <listitem>
+     <para>
+      Server name of the user mapping.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      Change options for the user mapping. The new options override
+      any previously specified
+      options.  <literal>ADD</>, <literal>SET</>, and <literal>DROP</>
+      specify the action to be performed.  <literal>ADD</> is assumed
+      if no operation is explicitly specified.  Option names must be
+      unique; options are also validated by the server's foreign-data
+      wrapper.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Change the password for user mapping <literal>bob</>, server<literal> foo</>:
+<programlisting>
+ALTER USER MAPPING FOR bob SERVER foo OPTIONS (user 'bob', password 'public');
+</programlisting>
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>ALTER USER MAPPING</command> conforms to ISO/IEC 9075-9
+   (SQL/MED).  There is a subtle syntax issue: The standard omits
+   the <literal>FOR</literal> key word.  Since both <literal>CREATE
+   USER MAPPING</literal> and <literal>DROP USER MAPPING</literal> use
+   <literal>FOR</literal> in analogous positions, and IBM DB2 (being
+   the other major SQL/MED implementation) also requires it
+   for <literal>ALTER USER MAPPING</literal>, PostgreSQL diverges from
+   the standard here in the interest of consistency and
+   interoperability.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createusermapping" endterm="sql-createusermapping-title"></member>
+   <member><xref linkend="sql-dropusermapping" endterm="sql-dropusermapping-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/create_foreign_data_wrapper.sgml b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
new file mode 100644 (file)
index 0000000..d46e8e6
--- /dev/null
@@ -0,0 +1,185 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_foreign_data_wrapper.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATEFOREIGNDATAWRAPPER">
+ <refmeta>
+  <refentrytitle id="sql-createforeigndatawrapper-title">CREATE FOREIGN DATA WRAPPER</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE FOREIGN DATA WRAPPER</refname>
+  <refpurpose>define a new foreign-data wrapper</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-createforeigndatawrapper">
+  <primary>CREATE FOREIGN DATA WRAPPER</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
+    LIBRARY '<replaceable class="parameter">libraryname</replaceable>'
+    LANGUAGE C
+    [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>CREATE FOREIGN DATA WRAPPER</command> creates a new
+   foreign-data wrapper.  The user who defines a foreign-data wrapper
+   becomes its owner.
+  </para>
+
+  <para>
+   The foreign-data wrapper name must be unique within the database.
+  </para>
+
+  <para>
+   Only superusers can create foreign-data wrappers.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the foreign-data wrapper to be created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">libraryname</replaceable></term>
+    <listitem>
+     <para>
+      The name of the shared library implementing the foreign-data
+      wrapper.  The file name is specified in the same way as for
+      shared library names in <xref linkend="sql-createfunction"
+      endterm="sql-createfunction-title">; in particular, one can rely
+      on a search path and automatic addition of the system's standard
+      shared library file name extension.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>LANGUAGE C</literal></term>
+    <listitem>
+     <para>
+      Currently, only the C programming language is supported for
+      implementing foreign-data wrappers.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies options for the new foreign-data wrapper.
+      The allowed option names and values are specific to each foreign
+      data wrapper and are validated using the foreign-data wrapper
+      library.  Option names must be unique.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   At the moment, the foreign-data wrapper functionality is very
+   rudimentary.  The purpose of foreign-data wrappers, foreign
+   servers, and user mappings is to store this information in a
+   standard way so that it can be queried by interested applications.
+   The functionality to actually query external data does not exist
+   yet.
+  </para>
+
+  <para>
+   The C language API for foreign-data wrappers is currently not
+   documented, stable, or complete.  Would-be authors of functionality
+   interfacing with the SQL/MED functionality are advised to contact
+   the PostgreSQL developers.
+  </para>
+
+  <para>
+   There are currently two foreign-data wrapper libraries
+   provided: <filename>dummy_fdw</filename>, which does nothing and
+   could be useful for testing,
+   and <filename>postgresql_fdw</filename>, which accepts options
+   corresponding to <application>libpq</> connection parameters.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Create a foreign-data wrapper <literal>dummy</> with
+   library <literal>dummy_fdw</>:
+<programlisting>
+CREATE FOREIGN DATA WRAPPER dummy LIBRARY 'dummy_fdw' LANGUAGE C;
+</programlisting>
+  </para>
+
+  <para>
+   Create a foreign-data wrapper <literal>postgresql</> with
+   library <literal>postgresql_fdw</>:
+<programlisting>
+CREATE FOREIGN DATA WRAPPER postgresql LIBRARY 'postgresql_fdw' LANGUAGE C;
+</programlisting>
+  </para>
+
+  <para>
+   Create a foreign-data wrapper <literal>mywrapper</> with library
+   <literal>/home/bob/mywrapper.so</> and some options:
+<programlisting>
+CREATE FOREIGN DATA WRAPPER mywrapper
+    LIBRARY '/home/bob/mywrapper.so'
+    LANGUAGE C
+    OPTIONS (debug 'true');
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>CREATE FOREIGN DATA WRAPPER</command> conforms to ISO/IEC
+   9075-9 (SQL/MED), with the exception that
+   the <literal>LIBRARY</literal> clause is not optional in
+   PostgreSQL.
+  </para>
+
+  <para>
+   Note, however, that the SQL/MED functionality as a whole is not yet
+   conforming.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alterforeigndatawrapper" endterm="sql-alterforeigndatawrapper-title"></member>
+   <member><xref linkend="sql-dropforeigndatawrapper" endterm="sql-dropforeigndatawrapper-title"></member>
+   <member><xref linkend="sql-createserver" endterm="sql-createserver-title"></member>
+   <member><xref linkend="sql-createusermapping" endterm="sql-createusermapping-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/create_server.sgml b/doc/src/sgml/ref/create_server.sgml
new file mode 100644 (file)
index 0000000..894eecc
--- /dev/null
@@ -0,0 +1,140 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_server.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATESERVER">
+ <refmeta>
+  <refentrytitle id="sql-createserver-title">CREATE SERVER</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE SERVER</refname>
+  <refpurpose>define a new foreign server</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-createserver">
+  <primary>CREATE SERVER</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE SERVER <replaceable class="parameter">servername</replaceable> [ TYPE 'servertype' ] [ VERSION 'serverversion' ]
+    FOREIGN DATA WRAPPER <replaceable class="parameter">fdwname</replaceable>
+    [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>CREATE SERVER</command> defines a new foreign server.  The
+   user who defines the server becomes its owner.
+  </para>
+
+  <para>
+   The server name must be unique within database.
+  </para>
+
+  <para>
+   Creating a server requires <literal>USAGE</> privilege on the
+   foreign-data wrapper being used.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">servername</replaceable></term>
+    <listitem>
+     <para>
+      The name of the foreign server to be created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">servertype</replaceable></term>
+    <listitem>
+     <para>
+      Optional server type.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">serverversion</replaceable></term>
+    <listitem>
+     <para>
+      Optional server version.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">fdwname</replaceable></term>
+    <listitem>
+     <para>
+      The name of the foreign-data wrapper that manages the server.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies the options for the server.  The options
+      typically define the connection details of the server, but the
+      actual names and values are dependent on the server's
+      foreign-data wrapper.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Create a server <literal>foo</> that uses the built-in foreign-data
+   wrapper <literal>default</>:
+<programlisting>
+CREATE SERVER foo FOREIGN DATA WRAPPER "default";
+</programlisting>
+  </para>
+
+  <para>
+   Create a server <literal>myserver</> that uses the
+   foreign-data wrapper <literal>pgsql</>:
+<programlisting>
+CREATE SERVER myserver FOREIGN DATA WRAPPER pgsql OPTIONS (host 'foo', dbname 'foodb', port '5432');
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>CREATE SERVER</command> conforms to ISO/IEC 9075-9 (SQL/MED).
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alterserver" endterm="sql-alterserver-title"></member>
+   <member><xref linkend="sql-dropserver" endterm="sql-dropserver-title"></member>
+   <member><xref linkend="sql-createforeigndatawrapper" endterm="sql-createforeigndatawrapper-title"></member>
+   <member><xref linkend="sql-createusermapping" endterm="sql-createusermapping-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/create_user_mapping.sgml b/doc/src/sgml/ref/create_user_mapping.sgml
new file mode 100644 (file)
index 0000000..07cfb0a
--- /dev/null
@@ -0,0 +1,111 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_user_mapping.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATEUSERMAPPING">
+ <refmeta>
+  <refentrytitle id="sql-createusermapping-title">CREATE USER MAPPING</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE USER MAPPING</refname>
+  <refpurpose>define a new mapping of a user to a foreign server</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-createusermapping">
+  <primary>CREATE USER MAPPING</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE USER MAPPING FOR { <replaceable class="parameter">username</replaceable> | USER | CURRENT_USER | PUBLIC }
+    SERVER <replaceable class="parameter">servername</replaceable>
+    [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [ , ... ] ) ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>CREATE USER MAPPING</command> defines a mapping of a user
+   to a foreign server.  You must be the owner of the server to define
+   user mappings for it.
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">username</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing user that is mapped to foreign server.
+      <literal>CURRENT_USER</> and <literal>USER</> match the name of
+      the current user.  <literal>PUBLIC</> is used to match all
+      present and future user names in the system.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">servername</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing server for which the user mapping is
+      to be created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      This clause specifies the options of the user mapping.  The
+      options typically define the actual user name and password of
+      the mapping.  Option names must be unque.  The allowed option
+      names and values are specific to the server's foreign-data wrapper.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Create a user mapping for user <literal>bob</>, server <literal>foo</>:
+<programlisting>
+CREATE USER MAPPING FOR bob SERVER foo OPTIONS (user 'bob', password 'secret');
+</programlisting>
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>CREATE USER MAPPING</command> conforms to ISO/IEC 9075-9 (SQL/MED).
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alterusermapping" endterm="sql-alterusermapping-title"></member>
+   <member><xref linkend="sql-dropusermapping" endterm="sql-dropusermapping-title"></member>
+   <member><xref linkend="sql-createforeigndatawrapper" endterm="sql-createforeigndatawrapper-title"></member>
+   <member><xref linkend="sql-createserver" endterm="sql-createserver-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml b/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml
new file mode 100644 (file)
index 0000000..e28b790
--- /dev/null
@@ -0,0 +1,112 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/drop_foreign_data_wrapper.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DROPFOREIGNDATAWRAPPER">
+ <refmeta>
+  <refentrytitle id="sql-dropforeigndatawrapper-title">DROP FOREIGN DATA WRAPPER</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP FOREIGN DATA WRAPPER</refname>
+  <refpurpose>remove a foreign-data wrapper</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-dropforeigndatawrapper">
+  <primary>DROP FOREIGN DATA WRAPPER</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP FOREIGN DATA WRAPPER [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP FOREIGN DATA WRAPPER</command> removes an existing
+   foreign-data wrapper.  To execute this command, the current user
+   must be the owner of the foreign-data wrapper.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the foreign-data wrapper does not
+      exist.  A notice is issued in this case.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing foreign-data wrapper.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CASCADE</literal></term>
+    <listitem>
+     <para>
+      Automatically drop objects that depend on the foreign-data
+      wrapper (such as servers).
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+    <listitem>
+     <para>
+      Refuse to drop the foreign-data wrappers if any objects depend
+      on it.  This is the default.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Drop the foreign-data wrapper <literal>dbi</>:
+<programlisting>
+DROP FOREIGN DATA WRAPPER dbi;
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>DROP FOREIGN DATA WRAPPER</command> conforms to ISO/IEC
+   9075-9 (SQL/MED).  The <literal>IF EXISTS</> clause is
+   a <productname>PostgreSQL</> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createforeigndatawrapper" endterm="sql-createforeigndatawrapper-title"></member>
+   <member><xref linkend="sql-alterforeigndatawrapper" endterm="sql-alterforeigndatawrapper-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/drop_server.sgml b/doc/src/sgml/ref/drop_server.sgml
new file mode 100644 (file)
index 0000000..b9ddac5
--- /dev/null
@@ -0,0 +1,112 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/drop_server.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DROPSERVER">
+ <refmeta>
+  <refentrytitle id="sql-dropserver-title">DROP SERVER</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP SERVER</refname>
+  <refpurpose>remove a foreign server descriptor</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-dropserver">
+  <primary>DROP SERVER</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP SERVER [ IF EXISTS ] <replaceable class="parameter">servername</replaceable> [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP SERVER</command> removes an existing foreign server
+   descriptor.  To execute this command, the current user must be the
+   owner of the server.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the server does not exist.  A notice is
+      issued in this case.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">servername</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing server.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CASCADE</literal></term>
+    <listitem>
+     <para>
+      Automatically drop objects that depend on the server (such as
+      user mappings).
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+    <listitem>
+     <para>
+      Refuse to drop the server if any objects depend on it.  This is
+      the default.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Drop a server <literal>foo</> if it exists:
+<programlisting>
+DROP SERVER IF EXISTS foo;
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>DROP SERVER</command> conforms to ISO/IEC 9075-9
+   (SQL/MED).  The <literal>IF EXISTS</> clause is
+   a <productname>PostgreSQL</> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createserver" endterm="sql-createserver-title"></member>
+   <member><xref linkend="sql-alterserver" endterm="sql-alterserver-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/drop_user_mapping.sgml b/doc/src/sgml/ref/drop_user_mapping.sgml
new file mode 100644 (file)
index 0000000..c22dedb
--- /dev/null
@@ -0,0 +1,104 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/drop_user_mapping.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DROPUSERMAPPING">
+ <refmeta>
+  <refentrytitle id="sql-dropusermapping-title">DROP USER MAPPING</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP USER MAPPING</refname>
+  <refpurpose>remove a user mapping for a foreign server</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-dropusermapping">
+  <primary>DROP USER MAPPING</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP USER MAPPING [ IF EXISTS ] FOR { <replaceable class="parameter">username</replaceable> | USER | CURRENT_USER | PUBLIC } SERVER <replaceable class="parameter">servername</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP USER MAPPING</command> removes an existing user
+   mapping from foreign server.  To execute this command, the current
+   user must be the owner of the server containing the mapping.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the user mapping does not exist.  A
+      notice is issued in this case.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">username</replaceable></term>
+    <listitem>
+     <para>
+      User name of the mapping.  <literal>CURRENT_USER</>
+      and <literal>USER</> match the name of the current
+      user.  <literal>PUBLIC</> is used to match all present and
+      future user names in the system.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">servername</replaceable></term>
+    <listitem>
+     <para>
+      Server name of the user mapping.
+     </para>
+    </listitem>
+   </varlistentry>
+    </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Drop a user mapping <literal>bob</>, server <literal>foo</> if it exists:
+<programlisting>
+DROP USER MAPPING IF EXISTS FOR bob SERVER foo;
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>DROP USER MAPPING</command> conforms to ISO/IEC 9075-9
+   (SQL/MED).  The <literal>IF EXISTS</> clause is
+   a <productname>PostgreSQL</> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createusermapping" endterm="sql-createusermapping-title"></member>
+   <member><xref linkend="sql-alterusermapping" endterm="sql-alterusermapping-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
index 5c907e1ef0518ebb20ebc88f46a50ec8cacd1dbb..dafb8ffb523885fda8d99b1804a72d4380a8a10f 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.72 2008/11/14 10:22:47 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.73 2008/12/19 16:25:16 petere Exp $
 PostgreSQL documentation
 -->
 
@@ -35,6 +35,14 @@ GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [,...] | ALL [ PRIVILEGES ] }
     ON DATABASE <replaceable>dbname</replaceable> [, ...]
     TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
 
+GRANT { USAGE | ALL [ PRIVILEGES ] }
+    ON FOREIGN DATA WRAPPER <replaceable>fdwname</> [, ...]
+    TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+GRANT { USAGE | ALL [ PRIVILEGES ] }
+    ON FOREIGN SERVER <replaceable>servername</> [, ...]
+    TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
 GRANT { EXECUTE | ALL [ PRIVILEGES ] }
     ON FUNCTION <replaceable>funcname</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) [, ...]
     TO { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
@@ -61,10 +69,10 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable
   <para>
    The <command>GRANT</command> command has two basic variants: one
    that grants privileges on a database object (table, view, sequence,
-   database, function, procedural language, schema, or tablespace),
-   and one that grants membership in a role.  These variants are
-   similar in many ways, but they are different enough to be described
-   separately.
+   database, foreign-data wrapper, foreign server, function,
+   procedural language, schema, or tablespace), and one that grants
+   membership in a role.  These variants are similar in many ways, but
+   they are different enough to be described separately.
   </para>
 
   <para>
@@ -299,6 +307,14 @@ GRANT <replaceable class="PARAMETER">role</replaceable> [, ...] TO <replaceable
        For sequences, this privilege allows the use of the
        <function>currval</function> and <function>nextval</function> functions.
       </para>
+      <para>
+       For foreign-data wrappers, this privilege enables the grantee
+       to create new servers using that foreign-data wrapper.
+      </para>
+      <para>
+       For servers, this privilege enables the grantee to query the
+       options of the server and associated user mappings.
+      </para>
      </listitem>
     </varlistentry>
 
index fa7f68caf40ecaeb5d47acb361535eb7159e9c22..fb394b16eefe4bf3d6d5785f739948d75921dd4a 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.213 2008/12/01 09:20:37 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.214 2008/12/19 16:25:16 petere Exp $
 PostgreSQL documentation
 -->
 
@@ -946,6 +946,64 @@ testdb=&gt;
       </varlistentry>
 
 
+      <varlistentry>
+        <term><literal>\des [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
+        <term><literal>\des+ [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
+        <listitem>
+        <para>
+        Lists all foreign servers (mnemonic: <quote>external
+        servers</quote>).
+        If <replaceable class="parameter">pattern</replaceable> is
+        specified, only those servers whose name matches the pattern
+        are listed.  If the form <literal>\des+</literal> is used, a
+        full desription of each server is shown, including the
+        server's ACL, type, version, and options.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\deu [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
+        <term><literal>\deu+ [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
+        <listitem>
+        <para>
+        Lists all user mappings (mnemonic: <quote>external
+        users</quote>).
+        If <replaceable class="parameter">pattern</replaceable> is
+        specified, only those mappings whose user names match the
+        pattern are listed.  If the form <literal>\deu+</literal> is
+        used, additional information about each mapping is shown.
+        </para>
+
+        <caution>
+        <para>
+        <literal>\deu+</literal> might also display the user name and
+        password of the remote user, so care should be taken not to
+        disclose them.
+        </para>
+        </caution>
+        </listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+        <term><literal>\dew [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
+        <term><literal>\dew+ [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
+        <listitem>
+        <para>
+        Lists all foreign-data wrappers (mnemonic: <quote>external
+        wrappers</quote>).
+        If <replaceable class="parameter">pattern</replaceable> is
+        specified, only those foreign-data wrappers whose name matches
+        the pattern are listed.  If the form <literal>\dew+</literal>
+        is used, the ACL and options of the foreign-data wrapper are
+        also shown.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\df [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
         <term><literal>\df+ [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
index 11f576e0fb7746837e2d04e7f373ad7886ea6de9..c8e91e0a159aa2a14d94508418ea418032be7dd9 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.49 2008/11/14 10:22:47 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/revoke.sgml,v 1.50 2008/12/19 16:25:16 petere Exp $
 PostgreSQL documentation
 -->
 
@@ -41,6 +41,18 @@ REVOKE [ GRANT OPTION FOR ]
     FROM { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...]
     [ CASCADE | RESTRICT ]
 
+REVOKE [ GRANT OPTION FOR ]
+    { USAGE | ALL [ PRIVILEGES ] }
+    ON FOREIGN DATA WRAPPER <replaceable>fdwname</replaceable> [, ...]
+    FROM { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...]
+    [ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+    { USAGE | ALL [ PRIVILEGES ] }
+    ON FOREIGN SERVER <replaceable>servername</replaceable> [, ...]
+    FROM { [ GROUP ] <replaceable class="PARAMETER">rolename</replaceable> | PUBLIC } [, ...]
+    [ CASCADE | RESTRICT ]
+
 REVOKE [ GRANT OPTION FOR ]
     { EXECUTE | ALL [ PRIVILEGES ] }
     ON FUNCTION <replaceable>funcname</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) [, ...]
index a0aed8edf535689d54f3a1ac4eca16b8fddc4bfd..d3a862959d900c224ecd0207fcc86f652cfe1974 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.66 2008/03/27 17:24:16 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.67 2008/12/19 16:25:16 petere Exp $ -->
 
 <part id="reference">
  <title>Reference</title>
@@ -38,6 +38,7 @@
    &alterConversion;
    &alterDatabase;
    &alterDomain;
+   &alterForeignDataWrapper;
    &alterFunction;
    &alterGroup;
    &alterIndex;
@@ -48,6 +49,7 @@
    &alterRole;
    &alterSchema;
    &alterSequence;
+   &alterServer;
    &alterTable;
    &alterTableSpace;
    &alterTSConfig;
@@ -57,6 +59,7 @@
    &alterTrigger;
    &alterType;
    &alterUser;
+   &alterUserMapping;
    &alterView;
    &analyze;
    &begin;
@@ -73,6 +76,7 @@
    &createConversion;
    &createDatabase;
    &createDomain;
+   &createForeignDataWrapper;
    &createFunction;
    &createGroup;
    &createIndex;
@@ -84,6 +88,7 @@
    &createRule;
    &createSchema;
    &createSequence;
+   &createServer;
    &createTable;
    &createTableAs;
    &createTableSpace;
@@ -94,6 +99,7 @@
    &createTrigger;
    &createType;
    &createUser;
+   &createUserMapping;
    &createView;
    &deallocate;
    &declare;
    &dropConversion;
    &dropDatabase;
    &dropDomain;
+   &dropForeignDataWrapper;
    &dropFunction;
    &dropGroup;
    &dropIndex;
    &dropRule;
    &dropSchema;
    &dropSequence;
+   &dropServer;
    &dropTable;
    &dropTableSpace;
    &dropTSConfig;
    &dropTrigger;
    &dropType;
    &dropUser;
+   &dropUserMapping;
    &dropView;
    &end;
    &execute;
index 61950967fde651165d9bcbb986448722e99d1ca7..13cbeb973456f9aa94e3c4435fd89bbfcc11c15c 100644 (file)
@@ -4,7 +4,7 @@
 #
 # Copyright (c) 1994, Regents of the University of California
 #
-# $PostgreSQL: pgsql/src/Makefile,v 1.43 2008/03/18 16:24:50 petere Exp $
+# $PostgreSQL: pgsql/src/Makefile,v 1.44 2008/12/19 16:25:16 petere Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -19,6 +19,7 @@ all install installdirs uninstall distprep:
    $(MAKE) -C backend $@
    $(MAKE) -C backend/utils/mb/conversion_procs $@
    $(MAKE) -C backend/snowball $@
+   $(MAKE) -C backend/foreign $@-fdw
    $(MAKE) -C include $@
    $(MAKE) -C interfaces $@
    $(MAKE) -C bin $@
index 7fd613f6c9fc5d6b93efd2ed0daae88a9b910bf7..1903e554cf952580b5ceb16a13dbe1f8ba25336f 100644 (file)
@@ -5,7 +5,7 @@
 # Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
 # Portions Copyright (c) 1994, Regents of the University of California
 #
-# $PostgreSQL: pgsql/src/backend/Makefile,v 1.130 2008/08/29 13:02:32 petere Exp $
+# $PostgreSQL: pgsql/src/backend/Makefile,v 1.131 2008/12/19 16:25:16 petere Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -14,7 +14,7 @@ subdir = src/backend
 top_builddir = ../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS = access bootstrap catalog parser commands executor lib libpq \
+SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
    main nodes optimizer port postmaster regex rewrite \
    storage tcop tsearch utils $(top_builddir)/src/timezone
 
index bb4c42135c554f2b0fc447ee142ac3e27523e0d9..58973c9a88991588372d59b42e8ddae702447d84 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Makefile for backend/catalog
 #
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.67 2008/11/19 10:34:51 heikki Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.68 2008/12/19 16:25:16 petere Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -36,6 +36,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
    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_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
    toasting.h indexing.h \
     )
 
index f25c7608e41e4ee3d5f9e9207b1d44bf52f62570..cecfe84e7a3c41f03ac56e7c05976fee07cf15ca 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.149 2008/11/02 01:45:27 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.150 2008/12/19 16:25:16 petere Exp $
  *
  * NOTES
  *   See acl.h.
@@ -27,6 +27,8 @@
 #include "catalog/pg_authid.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
@@ -38,6 +40,7 @@
 #include "catalog/pg_ts_config.h"
 #include "catalog/pg_ts_dict.h"
 #include "commands/dbcommands.h"
+#include "foreign/foreign.h"
 #include "miscadmin.h"
 #include "parser/parse_func.h"
 #include "utils/acl.h"
@@ -50,6 +53,8 @@
 
 static void ExecGrant_Relation(InternalGrant *grantStmt);
 static void ExecGrant_Database(InternalGrant *grantStmt);
+static void ExecGrant_Fdw(InternalGrant *grantStmt);
+static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
 static void ExecGrant_Function(InternalGrant *grantStmt);
 static void ExecGrant_Language(InternalGrant *grantStmt);
 static void ExecGrant_Namespace(InternalGrant *grantStmt);
@@ -188,6 +193,12 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
        case ACL_KIND_TABLESPACE:
            whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
            break;
+       case ACL_KIND_FDW:
+           whole_mask = ACL_ALL_RIGHTS_FDW;
+           break;
+       case ACL_KIND_FOREIGN_SERVER:
+           whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+           break;
        default:
            elog(ERROR, "unrecognized object kind: %d", objkind);
            /* not reached, but keep compiler quiet */
@@ -323,6 +334,14 @@ ExecuteGrantStmt(GrantStmt *stmt)
            all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
            errormsg = gettext_noop("invalid privilege type %s for tablespace");
            break;
+       case ACL_OBJECT_FDW:
+           all_privileges = ACL_ALL_RIGHTS_FDW;
+           errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper");
+           break;
+       case ACL_OBJECT_FOREIGN_SERVER:
+           all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+           errormsg = gettext_noop("invalid privilege type %s for foreign server");
+           break;
        default:
            /* keep compiler quiet */
            all_privileges = ACL_NO_RIGHTS;
@@ -380,6 +399,12 @@ ExecGrantStmt_oids(InternalGrant *istmt)
        case ACL_OBJECT_DATABASE:
            ExecGrant_Database(istmt);
            break;
+       case ACL_OBJECT_FDW:
+           ExecGrant_Fdw(istmt);
+           break;
+       case ACL_OBJECT_FOREIGN_SERVER:
+           ExecGrant_ForeignServer(istmt);
+           break;
        case ACL_OBJECT_FUNCTION:
            ExecGrant_Function(istmt);
            break;
@@ -520,6 +545,24 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
                heap_close(relation, AccessShareLock);
            }
            break;
+       case ACL_OBJECT_FDW:
+           foreach(cell, objnames)
+           {
+               char   *fdwname = strVal(lfirst(cell));
+               Oid     fdwid = GetForeignDataWrapperOidByName(fdwname, false);
+
+               objects = lappend_oid(objects, fdwid);
+           }
+           break;
+       case ACL_OBJECT_FOREIGN_SERVER:
+           foreach(cell, objnames)
+           {
+               char   *srvname = strVal(lfirst(cell));
+               Oid     srvid = GetForeignServerOidByName(srvname, false);
+
+               objects = lappend_oid(objects, srvid);
+           }
+           break;
        default:
            elog(ERROR, "unrecognized GrantStmt.objtype: %d",
                 (int) objtype);
@@ -840,6 +883,239 @@ ExecGrant_Database(InternalGrant *istmt)
    heap_close(relation, RowExclusiveLock);
 }
 
+static void
+ExecGrant_Fdw(InternalGrant *istmt)
+{
+   Relation    relation;
+   ListCell   *cell;
+
+   if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+       istmt->privileges = ACL_ALL_RIGHTS_FDW;
+
+   relation = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+   foreach(cell, istmt->objects)
+   {
+       Oid         fdwid = lfirst_oid(cell);
+       Form_pg_foreign_data_wrapper pg_fdw_tuple;
+       Datum       aclDatum;
+       bool        isNull;
+       AclMode     avail_goptions;
+       AclMode     this_privileges;
+       Acl        *old_acl;
+       Acl        *new_acl;
+       Oid         grantorId;
+       Oid         ownerId;
+       HeapTuple   tuple;
+       HeapTuple   newtuple;
+       Datum       values[Natts_pg_foreign_data_wrapper];
+       bool        nulls[Natts_pg_foreign_data_wrapper];
+       bool        replaces[Natts_pg_foreign_data_wrapper];
+       int         noldmembers;
+       int         nnewmembers;
+       Oid        *oldmembers;
+       Oid        *newmembers;
+
+       tuple = SearchSysCache(FOREIGNDATAWRAPPEROID,
+                              ObjectIdGetDatum(fdwid),
+                              0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+           elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
+
+       pg_fdw_tuple = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
+
+       /*
+        * Get owner ID and working copy of existing ACL. If there's no ACL,
+        * substitute the proper default.
+        */
+       ownerId = pg_fdw_tuple->fdwowner;
+       aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
+                                  Anum_pg_foreign_data_wrapper_fdwacl,
+                                  &isNull);
+       if (isNull)
+           old_acl = acldefault(ACL_OBJECT_FDW, ownerId);
+       else
+           old_acl = DatumGetAclPCopy(aclDatum);
+
+       /* Determine ID to do the grant as, and available grant options */
+       select_best_grantor(GetUserId(), istmt->privileges,
+                           old_acl, ownerId,
+                           &grantorId, &avail_goptions);
+
+       /*
+        * Restrict the privileges to what we can actually grant, and emit the
+        * standards-mandated warning and error messages.
+        */
+       this_privileges =
+           restrict_and_check_grant(istmt->is_grant, avail_goptions,
+                                    istmt->all_privs, istmt->privileges,
+                                    fdwid, grantorId, ACL_KIND_FDW,
+                                    NameStr(pg_fdw_tuple->fdwname));
+
+       /*
+        * Generate new ACL.
+        *
+        * We need the members of both old and new ACLs so we can correct the
+        * shared dependency information.
+        */
+       noldmembers = aclmembers(old_acl, &oldmembers);
+
+       new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+                                      istmt->grant_option, istmt->behavior,
+                                      istmt->grantees, this_privileges,
+                                      grantorId, ownerId);
+
+       nnewmembers = aclmembers(new_acl, &newmembers);
+
+       /* finished building new ACL value, now insert it */
+       MemSet(values, 0, sizeof(values));
+       MemSet(nulls, false, sizeof(nulls));
+       MemSet(replaces, false, sizeof(replaces));
+
+       replaces[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
+       values[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(new_acl);
+
+       newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+                                    nulls, replaces);
+
+       simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+       /* keep the catalog indexes up to date */
+       CatalogUpdateIndexes(relation, newtuple);
+
+       /* Update the shared dependency ACL info */
+       updateAclDependencies(ForeignDataWrapperRelationId, HeapTupleGetOid(tuple),
+                             ownerId, istmt->is_grant,
+                             noldmembers, oldmembers,
+                             nnewmembers, newmembers);
+
+       ReleaseSysCache(tuple);
+
+       pfree(new_acl);
+
+       /* prevent error when processing duplicate objects */
+       CommandCounterIncrement();
+   }
+
+   heap_close(relation, RowExclusiveLock);
+}
+
+static void ExecGrant_ForeignServer(InternalGrant *istmt)
+{
+   Relation    relation;
+   ListCell   *cell;
+
+   if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+       istmt->privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
+
+   relation = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+   foreach(cell, istmt->objects)
+   {
+       Oid         srvid = lfirst_oid(cell);
+       Form_pg_foreign_server pg_server_tuple;
+       Datum       aclDatum;
+       bool        isNull;
+       AclMode     avail_goptions;
+       AclMode     this_privileges;
+       Acl        *old_acl;
+       Acl        *new_acl;
+       Oid         grantorId;
+       Oid         ownerId;
+       HeapTuple   tuple;
+       HeapTuple   newtuple;
+       Datum       values[Natts_pg_foreign_server];
+       bool        nulls[Natts_pg_foreign_server];
+       bool        replaces[Natts_pg_foreign_server];
+       int         noldmembers;
+       int         nnewmembers;
+       Oid        *oldmembers;
+       Oid        *newmembers;
+
+       tuple = SearchSysCache(FOREIGNSERVEROID,
+                              ObjectIdGetDatum(srvid),
+                              0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+           elog(ERROR, "cache lookup failed for foreign server %u", srvid);
+
+       pg_server_tuple = (Form_pg_foreign_server) GETSTRUCT(tuple);
+
+       /*
+        * Get owner ID and working copy of existing ACL. If there's no ACL,
+        * substitute the proper default.
+        */
+       ownerId = pg_server_tuple->srvowner;
+       aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
+                                  Anum_pg_foreign_server_srvacl,
+                                  &isNull);
+       if (isNull)
+           old_acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
+       else
+           old_acl = DatumGetAclPCopy(aclDatum);
+
+       /* Determine ID to do the grant as, and available grant options */
+       select_best_grantor(GetUserId(), istmt->privileges,
+                           old_acl, ownerId,
+                           &grantorId, &avail_goptions);
+
+       /*
+        * Restrict the privileges to what we can actually grant, and emit the
+        * standards-mandated warning and error messages.
+        */
+       this_privileges =
+           restrict_and_check_grant(istmt->is_grant, avail_goptions,
+                                    istmt->all_privs, istmt->privileges,
+                                    srvid, grantorId, ACL_KIND_FOREIGN_SERVER,
+                                    NameStr(pg_server_tuple->srvname));
+
+       /*
+        * Generate new ACL.
+        *
+        * We need the members of both old and new ACLs so we can correct the
+        * shared dependency information.
+        */
+       noldmembers = aclmembers(old_acl, &oldmembers);
+
+       new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+                                      istmt->grant_option, istmt->behavior,
+                                      istmt->grantees, this_privileges,
+                                      grantorId, ownerId);
+
+       nnewmembers = aclmembers(new_acl, &newmembers);
+
+       /* finished building new ACL value, now insert it */
+       MemSet(values, 0, sizeof(values));
+       MemSet(nulls, false, sizeof(nulls));
+       MemSet(replaces, false, sizeof(replaces));
+
+       replaces[Anum_pg_foreign_server_srvacl - 1] = true;
+       values[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(new_acl);
+
+       newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+                                    nulls, replaces);
+
+       simple_heap_update(relation, &newtuple->t_self, newtuple);
+
+       /* keep the catalog indexes up to date */
+       CatalogUpdateIndexes(relation, newtuple);
+
+       /* Update the shared dependency ACL info */
+       updateAclDependencies(ForeignServerRelationId, HeapTupleGetOid(tuple),
+                             ownerId, istmt->is_grant,
+                             noldmembers, oldmembers,
+                             nnewmembers, newmembers);
+
+       ReleaseSysCache(tuple);
+
+       pfree(new_acl);
+
+       /* prevent error when processing duplicate objects */
+       CommandCounterIncrement();
+   }
+
+   heap_close(relation, RowExclusiveLock);
+}
+
 static void
 ExecGrant_Function(InternalGrant *istmt)
 {
@@ -1428,7 +1704,11 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
    /* ACL_KIND_TSDICTIONARY */
    gettext_noop("permission denied for text search dictionary %s"),
    /* ACL_KIND_TSCONFIGURATION */
-   gettext_noop("permission denied for text search configuration %s")
+   gettext_noop("permission denied for text search configuration %s"),
+   /* ACL_KIND_FDW */
+   gettext_noop("permission denied for foreign-data wrapper %s"),
+   /* ACL_KIND_FOREIGN_SERVER */
+   gettext_noop("permission denied for foreign server %s")
 };
 
 static const char *const not_owner_msg[MAX_ACL_KIND] =
@@ -1460,7 +1740,11 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
    /* ACL_KIND_TSDICTIONARY */
    gettext_noop("must be owner of text search dictionary %s"),
    /* ACL_KIND_TSCONFIGURATION */
-   gettext_noop("must be owner of text search configuration %s")
+   gettext_noop("must be owner of text search configuration %s"),
+   /* ACL_KIND_FDW */
+   gettext_noop("must be owner of foreign-data wrapper %s"),
+   /* ACL_KIND_FOREIGN_SERVER */
+   gettext_noop("must be owner of foreign server %s")
 };
 
 
@@ -1534,6 +1818,10 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, Oid roleid,
            return pg_namespace_aclmask(table_oid, roleid, mask, how);
        case ACL_KIND_TABLESPACE:
            return pg_tablespace_aclmask(table_oid, roleid, mask, how);
+       case ACL_KIND_FDW:
+           return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
+       case ACL_KIND_FOREIGN_SERVER:
+           return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
        default:
            elog(ERROR, "unrecognized objkind: %d",
                 (int) objkind);
@@ -1962,6 +2250,131 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
    return result;
 }
 
+/*
+ * Exported routine for examining a user's privileges for a foreign
+ * data wrapper
+ */
+AclMode
+pg_foreign_data_wrapper_aclmask(Oid fdw_oid, Oid roleid,
+                               AclMode mask, AclMaskHow how)
+{
+   AclMode     result;
+   HeapTuple   tuple;
+   Datum       aclDatum;
+   bool        isNull;
+   Acl        *acl;
+   Oid         ownerId;
+
+   Form_pg_foreign_data_wrapper fdwForm;
+
+   /* Bypass permission checks for superusers */
+   if (superuser_arg(roleid))
+       return mask;
+
+   /*
+    * Must get the FDW's tuple from pg_foreign_data_wrapper
+    */
+   tuple = SearchSysCache(FOREIGNDATAWRAPPEROID,
+                          ObjectIdGetDatum(fdw_oid),
+                          0, 0, 0);
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+                (errmsg("foreign-data wrapper with OID %u does not exist",
+                       fdw_oid)));
+   fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
+
+   /*
+    * Normal case: get the FDW's ACL from pg_foreign_data_wrapper
+    */
+   ownerId = fdwForm->fdwowner;
+
+   aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
+                              Anum_pg_foreign_data_wrapper_fdwacl, &isNull);
+   if (isNull)
+   {
+       /* No ACL, so build default ACL */
+       acl = acldefault(ACL_OBJECT_FDW, ownerId);
+       aclDatum = (Datum) 0;
+   }
+   else
+   {
+       /* detoast rel's ACL if necessary */
+       acl = DatumGetAclP(aclDatum);
+   }
+
+   result = aclmask(acl, roleid, ownerId, mask, how);
+
+   /* if we have a detoasted copy, free it */
+   if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+       pfree(acl);
+
+   ReleaseSysCache(tuple);
+
+   return result;
+}
+
+/*
+ * Exported routine for examining a user's privileges for a foreign
+ * server.
+ */
+AclMode
+pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
+                         AclMode mask, AclMaskHow how)
+{
+   AclMode     result;
+   HeapTuple   tuple;
+   Datum       aclDatum;
+   bool        isNull;
+   Acl        *acl;
+   Oid         ownerId;
+
+   Form_pg_foreign_server srvForm;
+
+   /* Bypass permission checks for superusers */
+   if (superuser_arg(roleid))
+       return mask;
+
+   /*
+    * Must get the FDW's tuple from pg_foreign_data_wrapper
+    */
+   tuple = SearchSysCache(FOREIGNSERVEROID,
+                          ObjectIdGetDatum(srv_oid),
+                          0, 0, 0);
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+                (errmsg("foreign server with OID %u does not exist",
+                       srv_oid)));
+   srvForm = (Form_pg_foreign_server) GETSTRUCT(tuple);
+
+   /*
+    * Normal case: get the foreign server's ACL from pg_foreign_server
+    */
+   ownerId = srvForm->srvowner;
+
+   aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
+                              Anum_pg_foreign_server_srvacl, &isNull);
+   if (isNull)
+   {
+       /* No ACL, so build default ACL */
+       acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
+       aclDatum = (Datum) 0;
+   }
+   else
+   {
+       /* detoast rel's ACL if necessary */
+       acl = DatumGetAclP(aclDatum);
+   }
+
+   result = aclmask(acl, roleid, ownerId, mask, how);
+
+   /* if we have a detoasted copy, free it */
+   if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+       pfree(acl);
+
+   ReleaseSysCache(tuple);
+
+   return result;
+}
 
 /*
  * Exported routine for checking a user's access privileges to a table
@@ -2039,6 +2452,31 @@ pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode)
        return ACLCHECK_NO_PRIV;
 }
 
+/*
+ * Exported routine for checking a user's access privileges to a foreign
+ * data wrapper
+ */
+AclResult
+pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode)
+{
+   if (pg_foreign_data_wrapper_aclmask(fdw_oid, roleid, mode, ACLMASK_ANY) != 0)
+       return ACLCHECK_OK;
+   else
+       return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a foreign
+ * server
+ */
+AclResult
+pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode)
+{
+   if (pg_foreign_server_aclmask(srv_oid, roleid, mode, ACLMASK_ANY) != 0)
+       return ACLCHECK_OK;
+   else
+       return ACLCHECK_NO_PRIV;
+}
 
 /*
  * Ownership check for a relation (specified by OID).
@@ -2364,6 +2802,34 @@ pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid)
    return has_privs_of_role(roleid, ownerId);
 }
 
+/*
+ * Ownership check for a foreign server (specified by OID).
+ */
+bool
+pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
+{
+   HeapTuple   tuple;
+   Oid         ownerId;
+
+   /* Superusers bypass all permission checking. */
+   if (superuser_arg(roleid))
+       return true;
+
+   tuple = SearchSysCache(FOREIGNSERVEROID,
+                          ObjectIdGetDatum(srv_oid),
+                          0, 0, 0);
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+              errmsg("foreign server with OID %u does not exist",
+                     srv_oid)));
+
+   ownerId = ((Form_pg_foreign_server) GETSTRUCT(tuple))->srvowner;
+
+   ReleaseSysCache(tuple);
+
+   return has_privs_of_role(roleid, ownerId);
+}
 
 /*
  * Ownership check for a database (specified by OID).
index 8cd1abdf1b95428008ecb2f3f31036f8b07c9ce3..2cbc19f5a0621dd63be004216399a31601c45031 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.82 2008/11/10 21:49:16 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.83 2008/12/19 16:25:17 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,6 +33,8 @@
 #include "catalog/pg_conversion_fn.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_depend.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
@@ -47,6 +49,7 @@
 #include "catalog/pg_ts_parser.h"
 #include "catalog/pg_ts_template.h"
 #include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
 #include "commands/comment.h"
 #include "commands/dbcommands.h"
 #include "commands/defrem.h"
@@ -55,6 +58,7 @@
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
 #include "commands/typecmds.h"
+#include "foreign/foreign.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parsetree.h"
@@ -1105,6 +1109,18 @@ doDeletion(const ObjectAddress *object)
            RemoveTSConfigurationById(object->objectId);
            break;
 
+       case OCLASS_USER_MAPPING:
+           RemoveUserMappingById(object->objectId);
+           break;
+
+       case OCLASS_FOREIGN_SERVER:
+           RemoveForeignServerById(object->objectId);
+           break;
+
+       case OCLASS_FDW:
+           RemoveForeignDataWrapperById(object->objectId);
+           break;
+
            /* OCLASS_ROLE, OCLASS_DATABASE, OCLASS_TBLSPACE not handled */
 
        default:
@@ -2005,6 +2021,18 @@ getObjectClass(const ObjectAddress *object)
        case TableSpaceRelationId:
            Assert(object->objectSubId == 0);
            return OCLASS_TBLSPACE;
+
+       case ForeignDataWrapperRelationId:
+           Assert(object->objectSubId == 0);
+           return OCLASS_FDW;
+
+       case ForeignServerRelationId:
+           Assert(object->objectSubId == 0);
+           return OCLASS_FOREIGN_SERVER;
+
+       case UserMappingRelationId:
+           Assert(object->objectSubId == 0);
+           return OCLASS_USER_MAPPING;
    }
 
    /* shouldn't get here */
@@ -2501,6 +2529,50 @@ getObjectDescription(const ObjectAddress *object)
                break;
            }
 
+       case OCLASS_FDW:
+           {
+               ForeignDataWrapper *fdw;
+
+               fdw = GetForeignDataWrapper(object->objectId);
+               appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
+               break;
+           }
+
+       case OCLASS_FOREIGN_SERVER:
+           {
+               ForeignServer *srv;
+
+               srv = GetForeignServer(object->objectId);
+               appendStringInfo(&buffer, _("server %s"), srv->servername);
+               break;
+           }
+
+       case OCLASS_USER_MAPPING:
+           {
+               HeapTuple       tup;
+               Oid             useid;
+               char           *usename;
+
+               tup = SearchSysCache(USERMAPPINGOID,
+                                    ObjectIdGetDatum(object->objectId),
+                                    0, 0, 0);
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "cache lookup failed for user mapping %u",
+                        object->objectId);
+
+               useid = ((Form_pg_user_mapping) GETSTRUCT(tup))->umuser;
+
+               ReleaseSysCache(tup);
+
+               if (OidIsValid(useid))
+                   usename = GetUserNameFromId(useid);
+               else
+                   usename = "public";
+
+               appendStringInfo(&buffer, _("user mapping for %s"), usename);
+               break;
+           }
+
        default:
            appendStringInfo(&buffer, "unrecognized object %u %u %d",
                             object->classId,
index 970b48b7dff3487898e06444acca14ef6995a803..2d9cdb8e2248d8b90aa6aab554e527e0fb10ab9c 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (c) 2003-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.46 2008/09/08 00:47:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.47 2008/12/19 16:25:17 petere Exp $
  */
 
 /*
@@ -1244,19 +1244,53 @@ GRANT SELECT ON role_table_grants TO PUBLIC;
  * ROLE_USAGE_GRANTS view
  */
 
--- See USAGE_PRIVILEGES.
-
 CREATE VIEW role_usage_grants AS
-    SELECT CAST(null AS sql_identifier) AS grantor,
-           CAST(null AS sql_identifier) AS grantee,
+
+    /* foreign-data wrappers */
+    SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor,
+           CAST(g_grantee.rolname AS sql_identifier) AS grantee,
            CAST(current_database() AS sql_identifier) AS object_catalog,
-           CAST(null AS sql_identifier) AS object_schema,
-           CAST(null AS sql_identifier) AS object_name,
-           CAST(null AS character_data) AS object_type,
+           CAST('' AS sql_identifier) AS object_schema,
+           CAST(fdw.fdwname AS sql_identifier) AS object_name,
+           CAST('FOREIGN DATA WRAPPER' AS character_data) AS object_type,
            CAST('USAGE' AS character_data) AS privilege_type,
-           CAST(null AS character_data) AS is_grantable
+           CAST(
+             CASE WHEN aclcontains(fdw.fdwacl,
+                                   makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', true))
+                  THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable
 
-    WHERE false;
+    FROM pg_foreign_data_wrapper fdw,
+         pg_authid u_grantor,
+         pg_authid g_grantee
+
+    WHERE aclcontains(fdw.fdwacl,
+                          makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', false))
+          AND (u_grantor.rolname IN (SELECT role_name FROM enabled_roles)
+               OR g_grantee.rolname IN (SELECT role_name FROM enabled_roles))
+
+    UNION ALL
+
+    /* foreign server */
+    SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor,
+           CAST(g_grantee.rolname AS sql_identifier) AS grantee,
+           CAST(current_database() AS sql_identifier) AS object_catalog,
+           CAST('' AS sql_identifier) AS object_schema,
+           CAST(srv.srvname AS sql_identifier) AS object_name,
+           CAST('FOREIGN SERVER' AS character_data) AS object_type,
+           CAST('USAGE' AS character_data) AS privilege_type,
+           CAST(
+             CASE WHEN aclcontains(srv.srvacl,
+                                   makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', true))
+                  THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable
+
+    FROM pg_foreign_server srv,
+         pg_authid u_grantor,
+         pg_authid g_grantee
+
+    WHERE aclcontains(srv.srvacl,
+                          makeaclitem(g_grantee.oid, u_grantor.oid, 'USAGE', false))
+          AND (u_grantor.rolname IN (SELECT role_name FROM enabled_roles)
+               OR g_grantee.rolname IN (SELECT role_name FROM enabled_roles));
 
 GRANT SELECT ON role_usage_grants TO PUBLIC;
 
@@ -2007,11 +2041,10 @@ GRANT SELECT ON triggers TO PUBLIC;
  * USAGE_PRIVILEGES view
  */
 
--- Of the things currently implemented in PostgreSQL, usage privileges
--- apply only to domains.  Since domains have no real privileges, we
--- represent all domains with implicit usage privilege here.
-
 CREATE VIEW usage_privileges AS
+
+    /* domains */
+    -- Domains have no real privileges, so we represent all domains with implicit usage privilege here.
     SELECT CAST(u.rolname AS sql_identifier) AS grantor,
            CAST('PUBLIC' AS sql_identifier) AS grantee,
            CAST(current_database() AS sql_identifier) AS object_catalog,
@@ -2027,7 +2060,65 @@ CREATE VIEW usage_privileges AS
 
     WHERE u.oid = t.typowner
           AND t.typnamespace = n.oid
-          AND t.typtype = 'd';
+          AND t.typtype = 'd'
+
+    UNION ALL
+
+    /* foreign-data wrappers */
+    SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor,
+           CAST(grantee.rolname AS sql_identifier) AS grantee,
+           CAST(current_database() AS sql_identifier) AS object_catalog,
+           CAST('' AS sql_identifier) AS object_schema,
+           CAST(fdw.fdwname AS sql_identifier) AS object_name,
+           CAST('FOREIGN DATA WRAPPER' AS character_data) AS object_type,
+           CAST('USAGE' AS character_data) AS privilege_type,
+           CAST(
+             CASE WHEN aclcontains(fdw.fdwacl,
+                                   makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', true))
+                  THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable
+
+    FROM pg_foreign_data_wrapper fdw,
+         pg_authid u_grantor,
+         (
+           SELECT oid, rolname FROM pg_authid
+           UNION ALL
+           SELECT 0::oid, 'PUBLIC'
+         ) AS grantee (oid, rolname)
+
+    WHERE aclcontains(fdw.fdwacl,
+                      makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', false))
+          AND (pg_has_role(u_grantor.oid, 'USAGE')
+               OR pg_has_role(grantee.oid, 'USAGE')
+               OR grantee.rolname = 'PUBLIC')
+
+    UNION ALL
+
+    /* foreign servers */
+    SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor,
+           CAST(grantee.rolname AS sql_identifier) AS grantee,
+           CAST(current_database() AS sql_identifier) AS object_catalog,
+           CAST('' AS sql_identifier) AS object_schema,
+           CAST(srv.srvname AS sql_identifier) AS object_name,
+           CAST('FOREIGN SERVER' AS character_data) AS object_type,
+           CAST('USAGE' AS character_data) AS privilege_type,
+           CAST(
+             CASE WHEN aclcontains(srv.srvacl,
+                                   makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', true))
+                  THEN 'YES' ELSE 'NO' END AS character_data) AS is_grantable
+
+    FROM pg_foreign_server srv,
+         pg_authid u_grantor,
+         (
+           SELECT oid, rolname FROM pg_authid
+           UNION ALL
+           SELECT 0::oid, 'PUBLIC'
+         ) AS grantee (oid, rolname)
+
+    WHERE aclcontains(srv.srvacl,
+                      makeaclitem(grantee.oid, u_grantor.oid, 'USAGE', false))
+          AND (pg_has_role(u_grantor.oid, 'USAGE')
+               OR pg_has_role(grantee.oid, 'USAGE')
+               OR grantee.rolname = 'PUBLIC');
 
 GRANT SELECT ON usage_privileges TO PUBLIC;
 
@@ -2313,3 +2404,139 @@ CREATE VIEW element_types AS
                     FROM data_type_privileges );
 
 GRANT SELECT ON element_types TO PUBLIC;
+
+
+-- SQL/MED views; these use section numbers from part 9 of the standard.
+
+/* Base view for foreign-data wrappers */
+CREATE VIEW _pg_foreign_data_wrappers AS
+    SELECT w.oid,
+           w.fdwowner,
+           w.fdwoptions,
+           CAST(current_database() AS sql_identifier) AS foreign_data_wrapper_catalog,
+           CAST(fdwname AS sql_identifier) AS foreign_data_wrapper_name,
+           CAST(u.rolname AS sql_identifier) AS authorization_identifier,
+           CAST(fdwlibrary AS character_data) AS library_name,
+           CAST('c' AS character_data) AS foreign_data_wrapper_language
+    FROM pg_foreign_data_wrapper w, pg_authid u
+    WHERE u.oid = w.fdwowner
+          AND (pg_has_role(fdwowner, 'USAGE')
+               OR has_foreign_data_wrapper_privilege(w.oid, 'USAGE'));
+
+
+/*
+ * 24.4
+ * FOREIGN_DATA_WRAPPER_OPTIONS view
+ */
+CREATE VIEW foreign_data_wrapper_options AS
+    SELECT foreign_data_wrapper_catalog,
+           foreign_data_wrapper_name,
+           CAST((pg_options_to_table(w.fdwoptions)).option_name AS sql_identifier) AS option_name,
+           CAST((pg_options_to_table(w.fdwoptions)).option_value AS character_data) AS option_value
+    FROM _pg_foreign_data_wrappers w;
+
+GRANT SELECT ON foreign_data_wrapper_options TO PUBLIC;
+
+
+/*
+ * 24.5
+ * FOREIGN_DATA_WRAPPERS view
+ */
+CREATE VIEW foreign_data_wrappers AS
+    SELECT foreign_data_wrapper_catalog,
+           foreign_data_wrapper_name,
+           authorization_identifier,
+           library_name,
+           foreign_data_wrapper_language
+    FROM _pg_foreign_data_wrappers w;
+
+GRANT SELECT ON foreign_data_wrappers TO PUBLIC;
+
+
+/* Base view for foreign servers */
+CREATE VIEW _pg_foreign_servers AS
+    SELECT s.oid,
+           s.srvoptions,
+           CAST(current_database() AS sql_identifier) AS foreign_server_catalog,
+           CAST(srvname AS sql_identifier) AS foreign_server_name,
+           w.foreign_data_wrapper_catalog,
+           w.foreign_data_wrapper_name,
+           CAST(srvtype AS character_data) AS foreign_server_type,
+           CAST(srvversion AS character_data) AS foreign_server_version,
+           CAST(u.rolname AS sql_identifier) AS authorization_identifier
+    FROM pg_foreign_server s, _pg_foreign_data_wrappers w, pg_authid u
+    WHERE w.oid = s.srvfdw
+          AND u.oid = s.srvowner
+          AND (pg_has_role(s.srvowner, 'USAGE')
+               OR has_server_privilege(s.oid, 'USAGE'));
+
+
+/*
+ * 24.6
+ * FOREIGN_SERVER_OPTIONS view
+ */
+CREATE VIEW foreign_server_options AS
+    SELECT foreign_server_catalog,
+           foreign_server_name,
+           CAST((pg_options_to_table(s.srvoptions)).option_name AS sql_identifier) AS option_name,
+           CAST((pg_options_to_table(s.srvoptions)).option_value AS character_data) AS option_value
+    FROM _pg_foreign_servers s;
+
+GRANT SELECT ON TABLE foreign_server_options TO PUBLIC;
+
+
+/*
+ * 24.7
+ * FOREIGN_SERVERS view
+ */
+CREATE VIEW foreign_servers AS
+    SELECT foreign_server_catalog,
+           foreign_server_name,
+           foreign_data_wrapper_catalog,
+           foreign_data_wrapper_name,
+           foreign_server_type,
+           foreign_server_version,
+           authorization_identifier
+    FROM _pg_foreign_servers;
+
+GRANT SELECT ON foreign_servers TO PUBLIC;
+
+
+/* Base view for user mappings */
+CREATE VIEW _pg_user_mappings AS
+    SELECT um.oid,
+           um.umoptions,
+           CAST(COALESCE(u.rolname,'PUBLIC') AS sql_identifier ) AS authorization_identifier,
+           s.foreign_server_catalog,
+           s.foreign_server_name
+    FROM pg_user_mapping um LEFT JOIN pg_authid u ON (u.oid = um.umuser),
+         _pg_foreign_servers s
+    WHERE s.oid = um.umserver;
+
+
+/*
+ * 24.12
+ * USER_MAPPING_OPTIONS view
+ */
+CREATE VIEW user_mapping_options AS
+    SELECT authorization_identifier,
+           foreign_server_catalog,
+           foreign_server_name,
+           CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
+           CAST((pg_options_to_table(um.umoptions)).option_value AS character_data) AS option_value
+    FROM _pg_user_mappings um;
+
+GRANT SELECT ON user_mapping_options TO PUBLIC;
+
+
+/*
+ * 24.13
+ * USER_MAPPINGS view
+ */
+CREATE VIEW user_mappings AS
+    SELECT authorization_identifier,
+           foreign_server_catalog,
+           foreign_server_name
+    FROM _pg_user_mappings;
+
+GRANT SELECT ON user_mappings TO PUBLIC;
index 402bc5e3919020ee7c76f8715a5cded6ab7dffb6..5b387456fd3762ddaaca65fb1089821edf57f300 100644 (file)
@@ -487,6 +487,31 @@ T652   SQL-dynamic statements in SQL routines          NO
 T653   SQL-schema statements in external routines          NO  
 T654   SQL-dynamic statements in external routines         NO  
 T655   Cyclically dependent routines           NO  
+M001   Datalinks           NO  
+M002   Datalinks via SQL/CLI           NO  
+M003   Datalinks via Embedded SQL          NO  
+M004   Foreign data support            NO  
+M005   Foreign schema support          NO  
+M006   GetSQLString routine            NO  
+M007   TransmitRequest         NO  
+M009   GetOpts and GetStatistics routines          NO  
+M010   Foreign data wrapper support            NO  
+M011   Datalinks via Ada           NO  
+M012   Datalinks via C         NO  
+M013   Datalinks via COBOL         NO  
+M014   Datalinks via Fortran           NO  
+M015   Datalinks via M         NO  
+M016   Datalinks via Pascal            NO  
+M017   Datalinks via PL/I          NO  
+M018   Foreign data wrapper interface routines in Ada          NO  
+M019   Foreign data wrapper interface routines in C            NO  
+M020   Foreign data wrapper interface routines in COBOL            NO  
+M021   Foreign data wrapper interface routines in Fortran          NO  
+M022   Foreign data wrapper interface routines in MUMPS            NO  
+M023   Foreign data wrapper interface routines in Pascal           NO  
+M024   Foreign data wrapper interface routines in PL/I         NO  
+M030   SQL-server foreign data support         NO  
+M031   Foreign data wrapper general routines           NO  
 X010   XML type            YES 
 X011   Arrays of XML type          YES 
 X012   Multisets of XML type           NO  
index 07c0809609d6836679f334a3bf6627d45bc5c743..4223e5d1940420c4b176429b2b020fddd6b84eca 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1996-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.56 2008/11/09 21:24:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.57 2008/12/19 16:25:17 petere Exp $
  */
 
 CREATE VIEW pg_roles AS 
@@ -381,6 +381,28 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_written_backend() AS buffers_backend,
         pg_stat_get_buf_alloc() AS buffers_alloc;
 
+CREATE VIEW pg_user_mappings AS
+    SELECT
+        U.oid       AS umid,
+        S.oid       AS srvid,
+        S.srvname   AS srvname,
+        U.umuser    AS umuser,
+        CASE WHEN U.umuser = 0 THEN
+            'public'
+        ELSE
+            A.rolname
+        END AS usename,
+        CASE WHEN pg_has_role(S.srvowner, 'USAGE') OR has_server_privilege(S.oid, 'USAGE') THEN
+            U.umoptions
+        ELSE
+            NULL
+        END AS umoptions
+    FROM pg_user_mapping U
+         LEFT JOIN pg_authid A ON (A.oid = U.umuser) JOIN
+        pg_foreign_server S ON (U.umserver = S.oid);
+
+REVOKE ALL on pg_user_mapping FROM public;
+
 -- Tsearch debug function.  Defined here because it'd be pretty unwieldy
 -- to put it into pg_proc.h
 
index 49aefde416458e3cf59ffa1e9c6206e8c09d7abd..e455e62c9aa8e69a501ae41c402a27c01abb0d75 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for backend/commands
 #
 # IDENTIFICATION
-#    $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.38 2008/02/19 10:30:07 petere Exp $
+#    $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.39 2008/12/19 16:25:17 petere Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
    conversioncmds.o copy.o \
-   dbcommands.o define.o discard.o explain.o functioncmds.o \
+   dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
    indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
    portalcmds.o prepare.o proclang.o \
    schemacmds.o sequence.o tablecmds.o tablespace.o trigger.o \
index fce328bcc404b3942f5a40359d40f17af5d16c1a..3772638766cad20ffb856ec1d18f919add28630b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.29 2008/06/15 01:25:53 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.30 2008/12/19 16:25:17 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -270,6 +270,15 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
            AlterTSConfigurationOwner(stmt->object, newowner);
            break;
 
+       case OBJECT_FDW:
+           AlterForeignDataWrapperOwner(strVal(linitial(stmt->object)),
+                                        newowner);
+           break;
+
+       case OBJECT_FOREIGN_SERVER:
+           AlterForeignServerOwner(strVal(linitial(stmt->object)), newowner);
+           break;
+
        default:
            elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
                 (int) stmt->objectType);
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
new file mode 100644 (file)
index 0000000..224a8f0
--- /dev/null
@@ -0,0 +1,1102 @@
+/*-------------------------------------------------------------------------
+ *
+ * foreigncmds.c
+ *   foreign-data wrapper/server creation/manipulation commands
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *   $PostgreSQL: pgsql/src/backend/commands/foreigncmds.c,v 1.1 2008/12/19 16:25:17 petere Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/reloptions.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
+#include "commands/defrem.h"
+#include "foreign/foreign.h"
+#include "miscadmin.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Convert a DefElem list to the text array format that is used in
+ * pg_foreign_data_wrapper, pg_foreign_server, and pg_user_mapping.
+ *
+ * Note: The array is usually stored to database without further
+ * processing, hence any validation should be done before this
+ * conversion.
+ */
+static Datum
+optionListToArray(List *options)
+{
+   ArrayBuildState    *astate = NULL;
+   ListCell           *cell;
+   text               *t;
+
+   foreach (cell, options)
+   {
+       DefElem    *def = lfirst(cell);
+       const char *value = "";
+       Size        len;
+
+       value = defGetString(def);
+       len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
+       t = palloc(len + 1);
+       SET_VARSIZE(t, len);
+       sprintf(VARDATA(t), "%s=%s", def->defname, value);
+
+       astate = accumArrayResult(astate, PointerGetDatum(t),
+                                 false, TEXTOID,
+                                 CurrentMemoryContext);
+   }
+
+   if (astate)
+       return makeArrayResult(astate, CurrentMemoryContext);
+
+   return PointerGetDatum(NULL);
+}
+
+
+/*
+ * Transform the list of OptionDefElem into list of generic options.
+ * The result is converted to array of text suitable for storing in
+ * options.
+ *
+ * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER
+ * MAPPING.  In the ALTER cases, oldOptions is the current text array
+ * of options.
+ */
+static Datum
+transformGenericOptions(Datum oldOptions,
+                       List *optionDefList,
+                       GenericOptionFlags flags,
+                       ForeignDataWrapper *fdw,
+                       OptionListValidatorFunc validateOptionList)
+{
+   List     *resultOptions = untransformRelOptions(oldOptions);
+   ListCell *optcell;
+
+   foreach (optcell, optionDefList)
+   {
+       ListCell        *cell;
+       ListCell        *prev = NULL;
+       OptionDefElem   *od = lfirst(optcell);
+
+       /*
+        * Find the element in resultOptions.  We need this for
+        * validation in all cases.
+        */
+       foreach (cell, resultOptions)
+       {
+           DefElem *def = lfirst(cell);
+
+           if (strcmp(def->defname, od->def->defname) == 0)
+               break;
+           else
+               prev = cell;
+       }
+
+       /*
+        * It is possible to perform multiple SET/DROP actions on the
+        * same option.  The standard permits this, as long as the
+        * options to be added are unique.
+        */
+
+       switch (od->alter_op)
+       {
+           case ALTER_OPT_DROP:
+               if (!cell)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_UNDEFINED_OBJECT),
+                            errmsg("option \"%s\" not found",
+                                   od->def->defname)));
+               resultOptions = list_delete_cell(resultOptions, cell, prev);
+               break;
+
+           case ALTER_OPT_SET:
+               if (!cell)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_UNDEFINED_OBJECT),
+                            errmsg("option \"%s\" not found",
+                                   od->def->defname)));
+               lfirst(cell) = od->def;
+               break;
+
+           case ALTER_OPT_ADD:
+               if (cell)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_DUPLICATE_OBJECT),
+                            errmsg("option \"%s\" provided more than once",
+                                   od->def->defname)));
+               resultOptions = lappend(resultOptions, od->def);
+               break;
+
+           default:
+               elog(ERROR, "unrecognized action %d on option \"%s\"",
+                    od->alter_op, od->def->defname);
+               break;
+       }
+   }
+
+   if (validateOptionList)
+       validateOptionList(fdw, flags, resultOptions);
+
+   return optionListToArray(resultOptions);
+}
+
+
+/*
+ * Convert the user mapping user name to OID
+ */
+static Oid
+GetUserOidFromMapping(const char *username, bool missing_ok)
+{
+    if (!username)
+        /* PUBLIC user mapping */
+        return InvalidOid;
+
+    if (strcmp(username, "current_user") == 0)
+        /* map to the owner */
+        return GetUserId();
+
+    /* map to provided user */
+    return missing_ok ? get_roleid(username) : get_roleid_checked(username);
+}
+
+
+/*
+ * Change foreign-data wrapper owner.
+ *
+ * Allow this only for superusers; also the new owner must be a
+ * superuser.
+ */
+void
+AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
+{
+   HeapTuple   tup;
+   Relation    rel;
+   Oid         fdwId;
+
+   Form_pg_foreign_data_wrapper form;
+
+    /* Must be a superuser to change a FDW owner */
+   if (!superuser())
+       ereport(ERROR,
+               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
+                       name),
+                errhint("Must be superuser to change owner of a foreign-data wrapper.")));
+
+    /* New owner must also be a superuser */
+   if (!superuser_arg(newOwnerId))
+       ereport(ERROR,
+               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
+                       name),
+                errhint("The owner of a foreign-data wrapper must be a superuser.")));
+
+   rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+   tup = SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME,
+                            CStringGetDatum(name),
+                            0, 0, 0);
+
+   if (!HeapTupleIsValid(tup))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+               errmsg("foreign-data wrapper \"%s\" does not exist", name)));
+
+   fdwId = HeapTupleGetOid(tup);
+   form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
+
+   if (form->fdwowner != newOwnerId)
+   {
+       form->fdwowner = newOwnerId;
+
+       simple_heap_update(rel, &tup->t_self, tup);
+       CatalogUpdateIndexes(rel, tup);
+
+       /* Update owner dependency reference */
+       changeDependencyOnOwner(ForeignDataWrapperRelationId,
+                               fdwId,
+                               newOwnerId);
+   }
+
+   heap_close(rel, NoLock);
+   heap_freetuple(tup);
+}
+
+
+/*
+ * Change foreign server owner
+ */
+void
+AlterForeignServerOwner(const char *name, Oid newOwnerId)
+{
+   HeapTuple   tup;
+   Relation    rel;
+   Oid         srvId;
+   AclResult   aclresult;
+   Form_pg_foreign_server form;
+
+   rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+   tup = SearchSysCacheCopy(FOREIGNSERVERNAME,
+                            CStringGetDatum(name),
+                            0, 0, 0);
+
+   if (!HeapTupleIsValid(tup))
+        ereport(ERROR,
+                (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("server \"%s\" does not exist", name)));
+
+   srvId = HeapTupleGetOid(tup);
+   form = (Form_pg_foreign_server) GETSTRUCT(tup);
+
+   if (form->srvowner != newOwnerId)
+   {
+       /* Superusers can always do it */
+       if (!superuser())
+       {
+           /* Must be owner */
+           if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+                              name);
+
+           /* Must be able to become new owner */
+           check_is_member_of_role(GetUserId(), newOwnerId);
+
+           /* New owner must have USAGE privilege on foreign-data wrapper */
+           aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE);
+           if (aclresult != ACLCHECK_OK)
+           {
+               ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
+
+               aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
+           }
+       }
+
+       form->srvowner = newOwnerId;
+
+       simple_heap_update(rel, &tup->t_self, tup);
+       CatalogUpdateIndexes(rel, tup);
+
+       /* Update owner dependency reference */
+       changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup),
+                               newOwnerId);
+   }
+
+   heap_close(rel, NoLock);
+   heap_freetuple(tup);
+}
+
+
+/*
+ * Create a foreign-data wrapper
+ */
+void
+CreateForeignDataWrapper(CreateFdwStmt *stmt)
+{
+   Relation        rel;
+   Datum           values[Natts_pg_foreign_data_wrapper];
+   bool            nulls[Natts_pg_foreign_data_wrapper];
+   HeapTuple       tuple;
+   Oid             fdwId;
+   Datum           fdwoptions = InvalidOid;
+   Oid             ownerId;
+   ForeignDataWrapperLibrary  *fdwlib;
+
+   /* Must be super user */
+   if (!superuser())
+       ereport(ERROR,
+               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                errmsg("permission denied to create foreign-data wrapper \"%s\"",
+                       stmt->fdwname),
+                errhint("Must be superuser to create a foreign-data wrapper.")));
+
+   /* For now the owner cannot be specified on create. Use effective user ID. */
+   ownerId = GetUserId();
+
+   /*
+    * Check that there is no other foreign-data wrapper by this name.
+    */
+   if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
+       ereport(ERROR,
+               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                errmsg("foreign-data wrapper \"%s\" already exists",
+                       stmt->fdwname)));
+
+   /*
+    * Insert tuple into pg_foreign_data_wrapper.
+    */
+   rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+   MemSet(nulls, false, Natts_pg_foreign_data_wrapper);
+
+   values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
+       DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
+   values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
+   values[Anum_pg_foreign_data_wrapper_fdwlibrary - 1] = CStringGetTextDatum(stmt->library);
+   nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
+
+   /*
+    * See if the FDW library loads at all. We also might want to use it
+    * later for validating the options.
+    */
+   fdwlib = GetForeignDataWrapperLibrary(stmt->library);
+
+   fdwoptions = transformGenericOptions(0, stmt->options, FdwOpt, NULL,
+                                        fdwlib->validateOptionList);
+
+   if (OidIsValid(fdwoptions))
+       values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
+   else
+       nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
+
+   tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+   fdwId = simple_heap_insert(rel, tuple);
+   CatalogUpdateIndexes(rel, tuple);
+
+   heap_freetuple(tuple);
+
+   recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
+
+   heap_close(rel, NoLock);
+}
+
+
+/*
+ * Alter foreign-data wrapper
+ */
+void
+AlterForeignDataWrapper(AlterFdwStmt *stmt)
+{
+   Relation    rel;
+   HeapTuple   tp;
+   Datum       repl_val[Natts_pg_foreign_data_wrapper];
+   bool        repl_null[Natts_pg_foreign_data_wrapper];
+   bool        repl_repl[Natts_pg_foreign_data_wrapper];
+   Oid         fdwId;
+   bool        isnull;
+   Datum       datum;
+   ForeignDataWrapperLibrary *fdwlib;
+
+   /* Must be super user */
+   if (!superuser())
+       ereport(ERROR,
+               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                errmsg("permission denied to alter foreign-data wrapper \"%s\"",
+                       stmt->fdwname),
+                errhint("Must be superuser to alter a foreign-data wrapper.")));
+
+   tp = SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME,
+                           CStringGetDatum(stmt->fdwname),
+                           0, 0, 0);
+
+   if (!HeapTupleIsValid(tp))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+               errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
+
+   fdwId = HeapTupleGetOid(tp);
+
+   memset(repl_val, 0, sizeof(repl_val));
+   memset(repl_null, false, sizeof(repl_null));
+   memset(repl_repl, false, sizeof(repl_repl));
+
+   if (stmt->library)
+   {
+       /*
+        * New library specified -- load to see if valid.
+        */
+       fdwlib = GetForeignDataWrapperLibrary(stmt->library);
+
+       repl_val[Anum_pg_foreign_data_wrapper_fdwlibrary - 1] = CStringGetTextDatum(stmt->library);
+       repl_repl[Anum_pg_foreign_data_wrapper_fdwlibrary - 1] = true;
+
+       /*
+        * It could be that the options for the FDW, SERVER and USER MAPPING
+        * are no longer valid with the new library.  Warn about this.
+        */
+       ereport(WARNING,
+               (errmsg("changing the foreign-data wrapper library can cause "
+                       "the options for dependent objects to become invalid")));
+   }
+   else
+   {
+       /*
+        * No LIBRARY clause specified, but we need to load it for validating
+        * options.
+        */
+       datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+                               tp,
+                               Anum_pg_foreign_data_wrapper_fdwlibrary,
+                               &isnull);
+       fdwlib = GetForeignDataWrapperLibrary(TextDatumGetCString(datum));
+   }
+
+   /*
+    * Options specified, validate and update.
+    */
+   if (stmt->options)
+   {
+       /* Extract the current options */
+       datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+                               tp,
+                               Anum_pg_foreign_data_wrapper_fdwoptions,
+                               &isnull);
+
+       /* Transform the options */
+       datum = transformGenericOptions(datum, stmt->options, FdwOpt,
+                                       NULL, fdwlib->validateOptionList);
+
+       if (OidIsValid(datum))
+           repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = ObjectIdGetDatum(datum);
+       else
+           repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
+
+       repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
+   }
+
+   /* Everything looks good - update the tuple */
+
+   rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+   tp = heap_modify_tuple(tp, RelationGetDescr(rel),
+                         repl_val, repl_null, repl_repl);
+
+   simple_heap_update(rel, &tp->t_self, tp);
+   CatalogUpdateIndexes(rel, tp);
+
+   heap_close(rel, RowExclusiveLock);
+   heap_freetuple(tp);
+}
+
+
+/*
+ * Drop foreign-data wrapper
+ */
+void
+RemoveForeignDataWrapper(DropFdwStmt *stmt)
+{
+   Oid                 fdwId;
+   ObjectAddress       object;
+
+   fdwId = GetForeignDataWrapperOidByName(stmt->fdwname, true);
+
+   if (!superuser())
+       ereport(ERROR,
+               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+               errmsg("permission denied to drop foreign-data wrapper \"%s\"",
+                   stmt->fdwname),
+               errhint("Must be superuser to drop a foreign-data wrapper.")));
+
+   if (!OidIsValid(fdwId))
+   {
+       if (!stmt->missing_ok)
+           ereport(ERROR,
+                   (errcode(ERRCODE_UNDEFINED_OBJECT),
+                   errmsg("foreign-data wrapper \"%s\" does not exist",
+                   stmt->fdwname)));
+
+       /* IF EXISTS specified, just note it */
+       ereport(NOTICE,
+               (errmsg("foreign-data wrapper \"%s\" does not exist, skipping",
+                   stmt->fdwname)));
+       return;
+   }
+
+   /*
+    * Do the deletion
+    */
+   object.classId = ForeignDataWrapperRelationId;
+   object.objectId = fdwId;
+   object.objectSubId = 0;
+
+   performDeletion(&object, stmt->behavior);
+}
+
+
+/*
+ * Drop foreign-data wrapper by OID
+ */
+void
+RemoveForeignDataWrapperById(Oid fdwId)
+{
+   HeapTuple   tp;
+   Relation    rel;
+
+   rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
+   tp = SearchSysCache(FOREIGNDATAWRAPPEROID,
+                       ObjectIdGetDatum(fdwId),
+                       0, 0, 0);
+
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwId);
+
+   simple_heap_delete(rel, &tp->t_self);
+
+   ReleaseSysCache(tp);
+
+   heap_close(rel, RowExclusiveLock);
+}
+
+
+/*
+ * Create a foreign server
+ */
+void
+CreateForeignServer(CreateForeignServerStmt *stmt)
+{
+   Relation        rel;
+   Datum           srvoptions = InvalidOid;
+   Datum           values[Natts_pg_foreign_server];
+   bool            nulls[Natts_pg_foreign_server];
+   HeapTuple       tuple;
+   Oid             srvId;
+   Oid             ownerId;
+   AclResult       aclresult;
+   ObjectAddress   myself;
+   ObjectAddress   referenced;
+   ForeignDataWrapper *fdw;
+
+   /* For now the owner cannot be specified on create. Use effective user ID. */
+   ownerId = GetUserId();
+
+   /*
+    * Check that there is no other foreign server by this name.
+    */
+   if (GetForeignServerByName(stmt->servername, true) != NULL)
+       ereport(ERROR,
+               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                errmsg("server \"%s\" already exists",
+                       stmt->servername)));
+
+   /*
+    * Check that the FDW exists and that we have USAGE on it.
+    * Also get the actual FDW for option validation etc.
+    */
+   fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
+
+   aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
+   if (aclresult != ACLCHECK_OK)
+       aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
+
+   /*
+    * Insert tuple into pg_foreign_server.
+    */
+   rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+   MemSet(nulls, false, Natts_pg_foreign_server);
+
+   values[Anum_pg_foreign_server_srvname - 1] =
+       DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
+   values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
+   values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
+
+   /* Add server type if supplied */
+   if (stmt->servertype)
+       values[Anum_pg_foreign_server_srvtype - 1] =
+           CStringGetTextDatum(stmt->servertype);
+   else
+       nulls[Anum_pg_foreign_server_srvtype - 1] = true;
+
+   /* Add server version if supplied */
+   if (stmt->version)
+       values[Anum_pg_foreign_server_srvversion - 1] =
+           CStringGetTextDatum(stmt->version);
+   else
+       nulls[Anum_pg_foreign_server_srvversion - 1] = true;
+
+   /* Start with a blank acl */
+   nulls[Anum_pg_foreign_server_srvacl - 1] = true;
+
+   /* Add server options */
+   srvoptions = transformGenericOptions(0, stmt->options, ServerOpt, fdw,
+                                        fdw->lib->validateOptionList);
+
+   if (OidIsValid(srvoptions))
+       values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
+   else
+       nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
+
+   tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+   srvId = simple_heap_insert(rel, tuple);
+
+   CatalogUpdateIndexes(rel, tuple);
+
+   heap_freetuple(tuple);
+
+   /* Add dependency on FDW and owner */
+   myself.classId = ForeignServerRelationId;
+   myself.objectId = srvId;
+   myself.objectSubId = 0;
+
+   referenced.classId = ForeignDataWrapperRelationId;
+   referenced.objectId = fdw->fdwid;
+   referenced.objectSubId = 0;
+   recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+   recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
+
+   heap_close(rel, NoLock);
+}
+
+
+/*
+ * Alter foreign server
+ */
+void
+AlterForeignServer(AlterForeignServerStmt *stmt)
+{
+   Relation    rel;
+   HeapTuple   tp;
+   Datum       repl_val[Natts_pg_foreign_server];
+   bool        repl_null[Natts_pg_foreign_server];
+   bool        repl_repl[Natts_pg_foreign_server];
+   Oid         srvId;
+   Form_pg_foreign_server  srvForm;
+
+   tp = SearchSysCacheCopy(FOREIGNSERVERNAME,
+                           CStringGetDatum(stmt->servername),
+                           0, 0, 0);
+
+   if (!HeapTupleIsValid(tp))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+               errmsg("server \"%s\" does not exist", stmt->servername)));
+
+   srvId = HeapTupleGetOid(tp);
+   srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
+
+   /*
+    * Only owner or a superuser can ALTER a SERVER.
+    */
+   if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+                      stmt->servername);
+
+   memset(repl_val, 0, sizeof(repl_val));
+   memset(repl_null, false, sizeof(repl_null));
+   memset(repl_repl, false, sizeof(repl_repl));
+
+   if (stmt->has_version)
+   {
+       /*
+        * Change the server VERSION string.
+        */
+       if (stmt->version)
+           repl_val[Anum_pg_foreign_server_srvversion - 1] =
+               CStringGetTextDatum(stmt->version);
+       else
+           repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
+
+       repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
+   }
+
+   if (stmt->options)
+   {
+       ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
+       Datum               datum;
+       bool                isnull;
+
+       /* Extract the current srvoptions */
+       datum = SysCacheGetAttr(FOREIGNSERVEROID,
+                               tp,
+                               Anum_pg_foreign_server_srvoptions,
+                               &isnull);
+
+       /* Prepare the options array */
+       datum = transformGenericOptions(datum, stmt->options, ServerOpt,
+                                       fdw, fdw->lib->validateOptionList);
+
+       if (OidIsValid(datum))
+           repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
+       else
+           repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
+
+       repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
+   }
+
+   /* Everything looks good - update the tuple */
+
+   rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+   tp = heap_modify_tuple(tp, RelationGetDescr(rel),
+                         repl_val, repl_null, repl_repl);
+
+   simple_heap_update(rel, &tp->t_self, tp);
+   CatalogUpdateIndexes(rel, tp);
+
+   heap_close(rel, RowExclusiveLock);
+   heap_freetuple(tp);
+}
+
+
+/*
+ * Drop foreign server
+ */
+void
+RemoveForeignServer(DropForeignServerStmt *stmt)
+{
+   Oid             srvId;
+   ObjectAddress   object;
+
+   srvId = GetForeignServerOidByName(stmt->servername, true);
+
+   if (!OidIsValid(srvId))
+   {
+       /* Server not found, complain or notice */
+       if (!stmt->missing_ok)
+           ereport(ERROR,
+                   (errcode(ERRCODE_UNDEFINED_OBJECT),
+                   errmsg("server \"%s\" does not exist", stmt->servername)));
+
+       /* IF EXISTS specified, just note it */
+       ereport(NOTICE,
+               (errmsg("server \"%s\" does not exist, skipping",
+                       stmt->servername)));
+       return;
+   }
+
+   /* Only allow DROP if the server is owned by the user. */
+   if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
+       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+                      stmt->servername);
+
+   object.classId = ForeignServerRelationId;
+   object.objectId = srvId;
+   object.objectSubId = 0;
+
+   performDeletion(&object, stmt->behavior);
+}
+
+
+/*
+ * Drop foreign server by OID
+ */
+void
+RemoveForeignServerById(Oid srvId)
+{
+   HeapTuple   tp;
+   Relation    rel;
+
+   rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+   tp = SearchSysCache(FOREIGNSERVEROID,
+                       ObjectIdGetDatum(srvId),
+                       0, 0, 0);
+
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for foreign server %u", srvId);
+
+   simple_heap_delete(rel, &tp->t_self);
+
+   ReleaseSysCache(tp);
+
+   heap_close(rel, RowExclusiveLock);
+}
+
+
+/*
+ * Create user mapping
+ */
+void
+CreateUserMapping(CreateUserMappingStmt *stmt)
+{
+   Relation            rel;
+   Datum               useoptions = InvalidOid;
+   Datum               values[Natts_pg_user_mapping];
+   bool                nulls[Natts_pg_user_mapping];
+   HeapTuple           tuple;
+   Oid                 useId;
+   Oid                 umId;
+   Oid                 ownerId;
+   ObjectAddress       myself;
+   ObjectAddress       referenced;
+   ForeignServer      *srv;
+   ForeignDataWrapper *fdw;
+
+   ownerId = GetUserId();
+
+   useId = GetUserOidFromMapping(stmt->username, false);
+
+   /*
+    * Check that the server exists and that the we own it.
+    */
+   srv = GetForeignServerByName(stmt->servername, false);
+
+   if (!pg_foreign_server_ownercheck(srv->serverid, ownerId))
+       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+                      stmt->servername);
+
+   /*
+    * Check that the user mapping is unique within server.
+    */
+   umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
+                          ObjectIdGetDatum(useId),
+                          ObjectIdGetDatum(srv->serverid),
+                          0, 0);
+   if (OidIsValid(umId))
+       ereport(ERROR,
+               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                errmsg("user mapping \"%s\" already exists for server %s",
+                       MappingUserName(useId),
+                       stmt->servername)));
+
+   fdw = GetForeignDataWrapper(srv->fdwid);
+
+   /*
+    * Insert tuple into pg_user_mapping.
+    */
+   rel = heap_open(UserMappingRelationId, RowExclusiveLock);
+
+   MemSet(nulls, false, Natts_pg_user_mapping);
+
+   values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
+   values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
+
+   /* Add user options */
+   useoptions = transformGenericOptions(0, stmt->options, UserMappingOpt,
+                                        fdw, fdw->lib->validateOptionList);
+
+   if (OidIsValid(useoptions))
+       values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
+   else
+       nulls[Anum_pg_user_mapping_umoptions - 1] = true;
+
+   tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+   umId = simple_heap_insert(rel, tuple);
+
+   CatalogUpdateIndexes(rel, tuple);
+
+   heap_freetuple(tuple);
+
+   /* Add dependency on the server */
+   myself.classId = UserMappingRelationId;
+   myself.objectId = umId;
+   myself.objectSubId = 0;
+
+   referenced.classId = ForeignServerRelationId;
+   referenced.objectId = srv->serverid;
+   referenced.objectSubId = 0;
+   recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+   if (OidIsValid(useId))
+       /* Record the mapped user dependency */
+       recordDependencyOnOwner(UserMappingRelationId, umId, useId);
+
+   heap_close(rel, NoLock);
+}
+
+
+/*
+ * Alter user mapping
+ */
+void
+AlterUserMapping(AlterUserMappingStmt *stmt)
+{
+   Relation    rel;
+   HeapTuple   tp;
+   Datum       repl_val[Natts_pg_user_mapping];
+   bool        repl_null[Natts_pg_user_mapping];
+   bool        repl_repl[Natts_pg_user_mapping];
+   Oid         useId;
+   Oid         umId;
+   ForeignServer   *srv;
+
+   useId = GetUserOidFromMapping(stmt->username, false);
+   srv = GetForeignServerByName(stmt->servername, false);
+
+   umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
+                         ObjectIdGetDatum(useId),
+                         ObjectIdGetDatum(srv->serverid),
+                         0, 0);
+   if (!OidIsValid(umId))
+   {
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+               errmsg("user mapping \"%s\" does not exist for the server",
+                   MappingUserName(useId))));
+   }
+
+   /*
+    * Must be owner of the server to alter user mapping.
+    */
+   if (!pg_foreign_server_ownercheck(srv->serverid, GetUserId()))
+       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+                      stmt->servername);
+
+   tp = SearchSysCacheCopy(USERMAPPINGOID,
+                           ObjectIdGetDatum(umId),
+                           0, 0, 0);
+
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for user mapping %u", umId);
+
+   memset(repl_val, 0, sizeof(repl_val));
+   memset(repl_null, false, sizeof(repl_null));
+   memset(repl_repl, false, sizeof(repl_repl));
+
+   if (stmt->options)
+   {
+       ForeignDataWrapper         *fdw;
+       Datum                       datum;
+       bool                        isnull;
+
+       /*
+        * Process the options.
+        */
+
+       fdw = GetForeignDataWrapper(srv->fdwid);
+
+       datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
+                               tp,
+                               Anum_pg_user_mapping_umoptions,
+                               &isnull);
+
+       /* Prepare the options array */
+       datum = transformGenericOptions(datum, stmt->options, UserMappingOpt,
+                                       fdw, fdw->lib->validateOptionList);
+
+       if (OidIsValid(datum))
+           repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
+       else
+           repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
+
+       repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
+   }
+
+   /* Everything looks good - update the tuple */
+
+   rel = heap_open(UserMappingRelationId, RowExclusiveLock);
+
+   tp = heap_modify_tuple(tp, RelationGetDescr(rel),
+                         repl_val, repl_null, repl_repl);
+
+   simple_heap_update(rel, &tp->t_self, tp);
+   CatalogUpdateIndexes(rel, tp);
+
+   heap_close(rel, RowExclusiveLock);
+   heap_freetuple(tp);
+}
+
+
+/*
+ * Drop user mapping
+ */
+void
+RemoveUserMapping(DropUserMappingStmt *stmt)
+{
+   ObjectAddress   object;
+   Oid             useId;
+   Oid             umId;
+   ForeignServer  *srv;
+
+   useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok);
+   srv = GetForeignServerByName(stmt->servername, true);
+
+   if (stmt->username && !OidIsValid(useId))
+   {
+       /*
+        * IF EXISTS specified, role not found and not public.
+        * Notice this and leave.
+        */
+       elog(NOTICE, "role \"%s\" does not exist, skipping", stmt->username);
+       return;
+   }
+
+   if (!srv)
+   {
+       if (!stmt->missing_ok)
+           ereport(ERROR,
+                   (errcode(ERRCODE_UNDEFINED_OBJECT),
+                   errmsg("server \"%s\" does not exist",
+                   stmt->servername)));
+       /* IF EXISTS, just note it */
+       ereport(NOTICE, (errmsg("server does not exist, skipping")));
+       return;
+   }
+
+   umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
+                         ObjectIdGetDatum(useId),
+                         ObjectIdGetDatum(srv->serverid),
+                         0, 0);
+
+   if (!OidIsValid(umId))
+   {
+       if (!stmt->missing_ok)
+           ereport(ERROR,
+                   (errcode(ERRCODE_UNDEFINED_OBJECT),
+                   errmsg("user mapping \"%s\" does not exist for the server",
+                       MappingUserName(useId))));
+
+       /* IF EXISTS specified, just note it */
+       ereport(NOTICE,
+               (errmsg("user mapping \"%s\" does not exist for the server, skipping",
+                   MappingUserName(useId))));
+       return;
+   }
+
+   /*
+    * Only allow DROP if we own the server.
+    */
+   if (!pg_foreign_server_ownercheck(srv->serverid, GetUserId()))
+   {
+       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
+                      srv->servername);
+   }
+
+   /*
+    * Do the deletion
+    */
+   object.classId = UserMappingRelationId;
+   object.objectId = umId;
+   object.objectSubId = 0;
+
+   performDeletion(&object, DROP_CASCADE);
+}
+
+
+/*
+ * Drop user mapping by OID.  This is called to clean up dependencies.
+ */
+void
+RemoveUserMappingById(Oid umId)
+{
+   HeapTuple   tp;
+   Relation    rel;
+
+   rel = heap_open(UserMappingRelationId, RowExclusiveLock);
+
+   tp = SearchSysCache(USERMAPPINGOID,
+                       ObjectIdGetDatum(umId),
+                       0, 0, 0);
+
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for user mapping %u", umId);
+
+   simple_heap_delete(rel, &tp->t_self);
+
+   ReleaseSysCache(tp);
+
+   heap_close(rel, RowExclusiveLock);
+}
diff --git a/src/backend/foreign/Makefile b/src/backend/foreign/Makefile
new file mode 100644 (file)
index 0000000..b3cc209
--- /dev/null
@@ -0,0 +1,25 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for foreign
+#
+# IDENTIFICATION
+#    $PostgreSQL: pgsql/src/backend/foreign/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/foreign
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS= foreign.o
+
+include $(top_srcdir)/src/backend/common.mk
+
+FDW = dummy postgresql
+
+$(addsuffix -fdw,all install installdirs uninstall distprep):
+   for dir in $(FDW); do $(MAKE) -C $$dir `echo $@ | sed 's/-fdw$$//'` || exit; done
+
+clean distclean maintainer-clean:
+   for dir in $(FDW); do $(MAKE) -C $$dir $@ || exit; done
diff --git a/src/backend/foreign/dummy/Makefile b/src/backend/foreign/dummy/Makefile
new file mode 100644 (file)
index 0000000..8a05ada
--- /dev/null
@@ -0,0 +1,27 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for dummy foreign-data wrapper
+#
+# IDENTIFICATION
+#    $PostgreSQL: pgsql/src/backend/foreign/dummy/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/foreign/dummy
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+NAME = dummy_fdw
+OBJS = dummy_fdw.o
+
+include $(top_srcdir)/src/Makefile.shlib
+
+all: all-shared-lib
+
+install: all install-lib
+
+installdirs: installdirs-lib
+
+clean distclean maintainer-clean: clean-lib
+   rm -f $(OBJS)
diff --git a/src/backend/foreign/dummy/dummy_fdw.c b/src/backend/foreign/dummy/dummy_fdw.c
new file mode 100644 (file)
index 0000000..2017c84
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * dummy_fdw.c
+ *        "dummy" foreign-data wrapper
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *        $PostgreSQL: pgsql/src/backend/foreign/dummy/dummy_fdw.c,v 1.1 2008/12/19 16:25:17 petere Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "foreign/foreign.h"
+
+PG_MODULE_MAGIC;
+
+/*
+ * This looks like a complete waste right now, but it is useful for
+ * testing, and will become more interesting as more parts of the
+ * interface are implemented.
+ */
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
new file mode 100644 (file)
index 0000000..2fdb84a
--- /dev/null
@@ -0,0 +1,389 @@
+/*-------------------------------------------------------------------------
+ *
+ * foreign.c
+ *        support for foreign-data wrappers, servers and user mappings.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *        $PostgreSQL: pgsql/src/backend/foreign/foreign.c,v 1.1 2008/12/19 16:25:17 petere Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
+#include "foreign/foreign.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/parsenodes.h"
+#include "utils/acl.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/syscache.h"
+
+
+extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
+
+
+/* list of currently loaded foreign-data wrapper interfaces */
+static List *loaded_fdw_interfaces = NIL;
+
+
+/*
+ * GetForeignDataWrapperLibrary - return the named FDW library.  If it
+ * is already loaded, use that.  Otherwise allocate, initialize, and
+ * store in cache.
+ */
+ForeignDataWrapperLibrary *
+GetForeignDataWrapperLibrary(const char *libname)
+{
+   MemoryContext                   oldcontext;
+   void                           *libhandle = NULL;
+   ForeignDataWrapperLibrary      *fdwl = NULL;
+   ListCell                       *cell;
+
+   /* See if we have the FDW library is already loaded */
+   foreach (cell, loaded_fdw_interfaces)
+   {
+       fdwl = lfirst(cell);
+       if (strcmp(fdwl->libname, libname) == 0)
+           return fdwl;
+   }
+
+   /*
+    * We don't have it yet, so load and add.  Attempt a load_file()
+    * first to filter out any missing or unloadable libraries.
+    */
+   load_file(libname, false);
+
+   oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+   fdwl = palloc(sizeof(*fdwl));
+   fdwl->libname = pstrdup(libname);
+   loaded_fdw_interfaces = lappend(loaded_fdw_interfaces, fdwl);
+
+   MemoryContextSwitchTo(oldcontext);
+
+   /*
+    * Now look up the foreign data wrapper functions.
+    */
+#define LOOKUP_FUNCTION(name) \
+   (void *)(libhandle ? \
+       lookup_external_function(libhandle, name) \
+       : load_external_function(fdwl->libname, name, false, &libhandle))
+
+   fdwl->validateOptionList = LOOKUP_FUNCTION("_pg_validateOptionList");
+
+   return fdwl;
+}
+
+
+/*
+ * GetForeignDataWrapper -  look up the foreign-data wrapper by OID.
+ *
+ * Here we also deal with loading the FDW library and looking up the
+ * actual functions.
+ */
+ForeignDataWrapper *
+GetForeignDataWrapper(Oid fdwid)
+{
+   Form_pg_foreign_data_wrapper    fdwform;
+   ForeignDataWrapper             *fdw;
+   Datum                           datum;
+   HeapTuple                       tp;
+   bool                            isnull;
+
+   tp = SearchSysCache(FOREIGNDATAWRAPPEROID,
+                       ObjectIdGetDatum(fdwid),
+                       0, 0, 0);
+
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
+
+   fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
+
+   fdw = palloc(sizeof(ForeignDataWrapper));
+   fdw->fdwid = fdwid;
+   fdw->owner = fdwform->fdwowner;
+   fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
+
+   /* Extract library name */
+   datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+                           tp,
+                           Anum_pg_foreign_data_wrapper_fdwlibrary,
+                           &isnull);
+   fdw->fdwlibrary = pstrdup(TextDatumGetCString(datum));
+
+   fdw->lib = GetForeignDataWrapperLibrary(fdw->fdwlibrary);
+
+   /* Extract the options */
+   datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+                           tp,
+                           Anum_pg_foreign_data_wrapper_fdwoptions,
+                           &isnull);
+   fdw->options = untransformRelOptions(datum);
+
+   ReleaseSysCache(tp);
+
+   return fdw;
+}
+
+
+/*
+ * GetForeignDataWrapperOidByName - look up the foreign-data wrapper
+ * OID by name.
+ */
+Oid
+GetForeignDataWrapperOidByName(const char *fdwname, bool missing_ok)
+{
+   Oid fdwId;
+
+   fdwId = GetSysCacheOid(FOREIGNDATAWRAPPERNAME,
+                          CStringGetDatum(fdwname),
+                          0, 0, 0);
+
+   if (!OidIsValid(fdwId) && !missing_ok)
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+               errmsg("foreign-data wrapper \"%s\" does not exist", fdwname)));
+
+   return fdwId;
+}
+
+
+/*
+ * GetForeignDataWrapperByName - look up the foreign-data wrapper
+ * definition by name.
+ */
+ForeignDataWrapper *
+GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
+{
+   Oid fdwId = GetForeignDataWrapperOidByName(fdwname, missing_ok);
+
+   if (!OidIsValid(fdwId) && missing_ok)
+       return NULL;
+
+   return GetForeignDataWrapper(fdwId);
+}
+
+
+/*
+ * GetForeignServer - look up the foreign server definition.
+ */
+ForeignServer *
+GetForeignServer(Oid serverid)
+{
+   Form_pg_foreign_server  serverform;
+   ForeignServer          *server;
+   HeapTuple               tp;
+   Datum                   datum;
+   bool                    isnull;
+
+   tp = SearchSysCache(FOREIGNSERVEROID,
+                       ObjectIdGetDatum(serverid),
+                       0, 0, 0);
+
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for foreign server %u", serverid);
+
+   serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
+
+   server = palloc(sizeof(ForeignServer));
+   server->serverid = serverid;
+   server->servername = pstrdup(NameStr(serverform->srvname));
+   server->owner = serverform->srvowner;
+   server->fdwid = serverform->srvfdw;
+
+   /* Extract server type */
+   datum = SysCacheGetAttr(FOREIGNSERVEROID,
+                           tp,
+                           Anum_pg_foreign_server_srvtype,
+                           &isnull);
+   server->servertype = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
+
+   /* Extract server version */
+   datum = SysCacheGetAttr(FOREIGNSERVEROID,
+                           tp,
+                           Anum_pg_foreign_server_srvversion,
+                           &isnull);
+   server->serverversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
+
+   /* Extract the srvoptions */
+   datum = SysCacheGetAttr(FOREIGNSERVEROID,
+                           tp,
+                           Anum_pg_foreign_server_srvoptions,
+                           &isnull);
+
+   /* untransformRelOptions does exactly what we want - avoid duplication */
+   server->options = untransformRelOptions(datum);
+
+   ReleaseSysCache(tp);
+
+   return server;
+}
+
+
+/*
+ * GetForeignServerByName - look up the foreign server oid by name.
+ */
+Oid
+GetForeignServerOidByName(const char *srvname, bool missing_ok)
+{
+   Oid serverid;
+
+   serverid = GetSysCacheOid(FOREIGNSERVERNAME,
+                             CStringGetDatum(srvname),
+                             0, 0, 0);
+
+   if (!OidIsValid(serverid) && !missing_ok)
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+               errmsg("server \"%s\" does not exist", srvname)));
+
+   return serverid;
+}
+
+
+/*
+ * GetForeignServerByName - look up the foreign server definition by name.
+ */
+ForeignServer *
+GetForeignServerByName(const char *srvname, bool missing_ok)
+{
+    Oid    serverid = GetForeignServerOidByName(srvname, missing_ok);
+
+   if (!OidIsValid(serverid) && missing_ok)
+       return NULL;
+
+   return GetForeignServer(serverid);
+}
+
+
+/*
+ * GetUserMapping - look up the user mapping.
+ *
+ * If no mapping is found for the supplied user, we also look for
+ * PUBLIC mappings (userid == InvalidOid).
+ */
+UserMapping *
+GetUserMapping(Oid userid, Oid serverid)
+{
+   Form_pg_user_mapping    umform;
+   Datum       datum;
+   HeapTuple   tp;
+   bool        isnull;
+   UserMapping *um;
+
+   tp = SearchSysCache(USERMAPPINGUSERSERVER,
+                       ObjectIdGetDatum(userid),
+                       ObjectIdGetDatum(serverid),
+                       0, 0);
+
+   if (!HeapTupleIsValid(tp))
+   {
+       /* Not found for the specific user -- try PUBLIC */
+       tp = SearchSysCache(USERMAPPINGUSERSERVER,
+                           ObjectIdGetDatum(InvalidOid),
+                           ObjectIdGetDatum(serverid),
+                           0, 0);
+   }
+
+   if (!HeapTupleIsValid(tp))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+               errmsg("user mapping not found for \"%s\"",
+               MappingUserName(userid))));
+
+   umform = (Form_pg_user_mapping) GETSTRUCT(tp);
+
+   /* Extract the umoptions */
+   datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
+                           tp,
+                           Anum_pg_user_mapping_umoptions,
+                           &isnull);
+
+   um = palloc(sizeof(UserMapping));
+   um->userid = userid;
+   um->serverid = serverid;
+   um->options = untransformRelOptions(datum);
+
+   ReleaseSysCache(tp);
+
+   return um;
+}
+
+
+/*
+ * deflist_to_tuplestore - Helper function to convert DefElem list to
+ * tuplestore usable in SRF.
+ */
+static void
+deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
+{
+   ListCell   *cell;
+   TupleDesc   tupdesc;
+   Tuplestorestate *tupstore;
+   Datum       values[2];
+   bool        nulls[2] = { 0 };
+   MemoryContext per_query_ctx;
+   MemoryContext oldcontext;
+
+   /* 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")));
+
+   per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+   oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+   /*
+    * Now prepare the result set.
+    */
+   tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+   tupstore = tuplestore_begin_heap(true, false, work_mem);
+   rsinfo->returnMode = SFRM_Materialize;
+   rsinfo->setResult = tupstore;
+   rsinfo->setDesc = tupdesc;
+
+   foreach (cell, options)
+   {
+       DefElem    *def = lfirst(cell);
+
+       values[0] = CStringGetTextDatum(def->defname);
+       values[1] = CStringGetTextDatum(((Value *)def->arg)->val.str);
+       tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+   }
+
+   /* clean up and return the tuplestore */
+   tuplestore_donestoring(tupstore);
+
+   MemoryContextSwitchTo(oldcontext);
+}
+
+
+/*
+ * Convert options array to name/value table.  Useful for information
+ * schema and pg_dump.
+ */
+Datum
+pg_options_to_table(PG_FUNCTION_ARGS)
+{
+   Datum array = PG_GETARG_DATUM(0);
+
+   deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, untransformRelOptions(array));
+
+   return (Datum) 0;
+}
diff --git a/src/backend/foreign/postgresql/Makefile b/src/backend/foreign/postgresql/Makefile
new file mode 100644 (file)
index 0000000..40ed90f
--- /dev/null
@@ -0,0 +1,27 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for postgresql foreign-data wrapper
+#
+# IDENTIFICATION
+#    $PostgreSQL: pgsql/src/backend/foreign/postgresql/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/foreign/postgresql
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+NAME = postgresql_fdw
+OBJS = postgresql_fdw.o
+
+include $(top_srcdir)/src/Makefile.shlib
+
+all: all-shared-lib
+
+install: all install-lib
+
+installdirs: installdirs-lib
+
+clean distclean maintainer-clean: clean-lib
+   rm -f $(OBJS)
diff --git a/src/backend/foreign/postgresql/postgresql_fdw.c b/src/backend/foreign/postgresql/postgresql_fdw.c
new file mode 100644 (file)
index 0000000..f1c881e
--- /dev/null
@@ -0,0 +1,123 @@
+/*-------------------------------------------------------------------------
+ *
+ * postgresql_fdw.c
+ *        foreign-data wrapper for postgresql (libpq) connections.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *        $PostgreSQL: pgsql/src/backend/foreign/postgresql/postgresql_fdw.c,v 1.1 2008/12/19 16:25:17 petere Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "nodes/value.h"
+#include "nodes/parsenodes.h"
+#include "nodes/makefuncs.h"
+#include "foreign/foreign.h"
+
+PG_MODULE_MAGIC;
+
+
+/*
+ * Describes the valid options for postgresql FDW, server and user mapping.
+ */
+typedef struct ConnectionOptions {
+   const char         *optname;        /* Option name */
+   GenericOptionFlags  optflags;       /* Option usage bitmap */
+} ConnectionOptions;
+
+/*
+ * Copied from fe-connect.c PQconninfoOptions.
+ *
+ * The list is small - don't bother with bsearch if it stays so.
+ */
+static ConnectionOptions libpq_conninfo_options[] = {
+   { "authtype",           ServerOpt       },
+   { "service",            ServerOpt       },
+   { "user",               UserMappingOpt  },
+   { "password",           UserMappingOpt  },
+   { "connect_timeout",    ServerOpt       },
+   { "dbname",             ServerOpt       },
+   { "host",               ServerOpt       },
+   { "hostaddr",           ServerOpt       },
+   { "port",               ServerOpt       },
+   { "tty",                ServerOpt       },
+   { "options",            ServerOpt       },
+   { "requiressl",         ServerOpt       },
+   { "sslmode",            ServerOpt       },
+   { "gsslib",             ServerOpt       },
+   { NULL,                 InvalidOpt      }
+};
+
+void _PG_fini(void);
+
+
+/*
+ * Check if the provided option is one of libpq conninfo options.
+ * We look at only options with matching flags.
+ */
+static bool
+is_conninfo_option(const char *option, GenericOptionFlags flags)
+{
+   ConnectionOptions *opt;
+
+   for (opt = libpq_conninfo_options; opt->optname != NULL; opt++)
+       if (flags & opt->optflags && strcmp(opt->optname, option) == 0)
+           return true;
+   return false;
+}
+
+/*
+ * Validate the generic option given to SERVER or USER MAPPING.
+ * Raise an ERROR if the option or its value is considered
+ * invalid.
+ *
+ * Valid server options are all libpq conninfo options except
+ * user and password -- these may only appear in USER MAPPING options.
+ */
+void
+_pg_validateOptionList(ForeignDataWrapper *fdw, GenericOptionFlags flags,
+                  List *options)
+{
+   ListCell *cell;
+
+   foreach (cell, options)
+   {
+       DefElem    *def = lfirst(cell);
+
+       if (!is_conninfo_option(def->defname, flags))
+       {
+           ConnectionOptions  *opt;
+           StringInfoData      buf;
+           const char         *objtype;
+
+           /*
+            * Unknown option specified, complain about it. Provide a hint
+            * with list of valid options for the object.
+            */
+           initStringInfo(&buf);
+           for (opt = libpq_conninfo_options; opt->optname != NULL; opt++)
+               if (flags & opt->optflags)
+                   appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
+                                    opt->optname);
+
+           if (flags & ServerOpt)
+               objtype = "server";
+           else if (flags & UserMappingOpt)
+               objtype = "user mapping";
+           else if (flags & FdwOpt)
+               objtype = "foreign-data wrapper";
+           else
+               objtype = "???";
+
+           ereport(ERROR,
+                   (errcode(ERRCODE_SYNTAX_ERROR),
+                    errmsg("invalid option \"%s\" to %s", def->defname, objtype),
+                    errhint("valid %s options are: %s", objtype, buf.data)));
+       }
+   }
+}
index 6a0c3374ce8703ee223b71d063e3f59b6db38009..86f555a03a617566e7c5aa1dcd24694b54a7ee44 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.415 2008/12/04 17:51:26 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.416 2008/12/19 16:25:17 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2033,6 +2033,17 @@ _copyDefElem(DefElem *from)
    return newnode;
 }
 
+static OptionDefElem *
+_copyOptionDefElem(OptionDefElem *from)
+{
+   OptionDefElem    *newnode = makeNode(OptionDefElem);
+
+   COPY_SCALAR_FIELD(alter_op);
+   COPY_NODE_FIELD(def);
+
+   return newnode;
+}
+
 static LockingClause *
 _copyLockingClause(LockingClause *from)
 {
@@ -2869,6 +2880,117 @@ _copyDropTableSpaceStmt(DropTableSpaceStmt *from)
    return newnode;
 }
 
+static CreateFdwStmt *
+_copyCreateFdwStmt(CreateFdwStmt *from)
+{
+   CreateFdwStmt *newnode = makeNode(CreateFdwStmt);
+
+   COPY_STRING_FIELD(fdwname);
+   COPY_STRING_FIELD(library);
+   COPY_NODE_FIELD(options);
+
+   return newnode;
+}
+
+static AlterFdwStmt *
+_copyAlterFdwStmt(AlterFdwStmt *from)
+{
+   AlterFdwStmt *newnode = makeNode(AlterFdwStmt);
+
+   COPY_STRING_FIELD(fdwname);
+   COPY_STRING_FIELD(library);
+   COPY_NODE_FIELD(options);
+
+   return newnode;
+}
+
+static DropFdwStmt *
+_copyDropFdwStmt(DropFdwStmt *from)
+{
+   DropFdwStmt *newnode = makeNode(DropFdwStmt);
+
+   COPY_STRING_FIELD(fdwname);
+   COPY_SCALAR_FIELD(missing_ok);
+   COPY_SCALAR_FIELD(behavior);
+
+   return newnode;
+}
+
+static CreateForeignServerStmt *
+_copyCreateForeignServerStmt(CreateForeignServerStmt *from)
+{
+   CreateForeignServerStmt *newnode = makeNode(CreateForeignServerStmt);
+
+   COPY_STRING_FIELD(servername);
+   COPY_STRING_FIELD(servertype);
+   COPY_STRING_FIELD(version);
+   COPY_STRING_FIELD(fdwname);
+   COPY_NODE_FIELD(options);
+
+   return newnode;
+}
+
+static AlterForeignServerStmt *
+_copyAlterForeignServerStmt(AlterForeignServerStmt *from)
+{
+   AlterForeignServerStmt *newnode = makeNode(AlterForeignServerStmt);
+
+   COPY_STRING_FIELD(servername);
+   COPY_STRING_FIELD(version);
+   COPY_NODE_FIELD(options);
+   COPY_SCALAR_FIELD(has_version);
+
+   return newnode;
+}
+
+static DropForeignServerStmt *
+_copyDropForeignServerStmt(DropForeignServerStmt *from)
+{
+   DropForeignServerStmt *newnode = makeNode(DropForeignServerStmt);
+
+   COPY_STRING_FIELD(servername);
+   COPY_SCALAR_FIELD(missing_ok);
+   COPY_SCALAR_FIELD(behavior);
+
+   return newnode;
+}
+
+static CreateUserMappingStmt *
+_copyCreateUserMappingStmt(CreateUserMappingStmt *from)
+{
+   CreateUserMappingStmt *newnode = makeNode(CreateUserMappingStmt);
+
+   COPY_STRING_FIELD(username);
+   COPY_STRING_FIELD(servername);
+   COPY_NODE_FIELD(options);
+
+   return newnode;
+}
+
+static AlterUserMappingStmt *
+_copyAlterUserMappingStmt(AlterUserMappingStmt *from)
+{
+   AlterUserMappingStmt *newnode = makeNode(AlterUserMappingStmt);
+
+   COPY_STRING_FIELD(username);
+   COPY_STRING_FIELD(servername);
+   COPY_NODE_FIELD(options);
+
+   return newnode;
+}
+
+static DropUserMappingStmt *
+_copyDropUserMappingStmt(DropUserMappingStmt *from)
+{
+   DropUserMappingStmt *newnode = makeNode(DropUserMappingStmt);
+
+   COPY_STRING_FIELD(username);
+   COPY_STRING_FIELD(servername);
+   COPY_SCALAR_FIELD(missing_ok);
+
+   return newnode;
+}
+
 static CreateTrigStmt *
 _copyCreateTrigStmt(CreateTrigStmt *from)
 {
@@ -3696,6 +3818,33 @@ copyObject(void *from)
        case T_DropTableSpaceStmt:
            retval = _copyDropTableSpaceStmt(from);
            break;
+       case T_CreateFdwStmt:
+           retval = _copyCreateFdwStmt(from);
+           break;
+       case T_AlterFdwStmt:
+           retval = _copyAlterFdwStmt(from);
+           break;
+       case T_DropFdwStmt:
+           retval = _copyDropFdwStmt(from);
+           break;
+       case T_CreateForeignServerStmt:
+           retval = _copyCreateForeignServerStmt(from);
+           break;
+       case T_AlterForeignServerStmt:
+           retval = _copyAlterForeignServerStmt(from);
+           break;
+       case T_DropForeignServerStmt:
+           retval = _copyDropForeignServerStmt(from);
+           break;
+       case T_CreateUserMappingStmt:
+           retval = _copyCreateUserMappingStmt(from);
+           break;
+       case T_AlterUserMappingStmt:
+           retval = _copyAlterUserMappingStmt(from);
+           break;
+       case T_DropUserMappingStmt:
+           retval = _copyDropUserMappingStmt(from);
+           break;
        case T_CreateTrigStmt:
            retval = _copyCreateTrigStmt(from);
            break;
@@ -3823,6 +3972,9 @@ copyObject(void *from)
        case T_DefElem:
            retval = _copyDefElem(from);
            break;
+       case T_OptionDefElem:
+           retval = _copyOptionDefElem(from);
+           break;
        case T_LockingClause:
            retval = _copyLockingClause(from);
            break;
index ebf8cea2f0d80f35f5a6764959f479f1869dabbc..e5e2bc4422644e1052dd2fd19328465113c4e7ff 100644 (file)
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.340 2008/12/04 17:51:26 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.341 2008/12/19 16:25:17 petere Exp $
  *
  *-------------------------------------------------------------------------
  */