Add syntax for FOREIGN TABLE and FDW HANDLER.
authorShigeru Hanada <[email protected]>
Wed, 15 Dec 2010 07:29:10 +0000 (16:29 +0900)
committerShigeru Hanada <[email protected]>
Wed, 15 Dec 2010 07:29:10 +0000 (16:29 +0900)
77 files changed:
contrib/pageinspect/rawpage.c
contrib/pgstattuple/pgstattuple.c
doc/src/sgml/catalogs.sgml
doc/src/sgml/information_schema.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/alter_default_privileges.sgml
doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
doc/src/sgml/ref/alter_foreign_table.sgml [new file with mode: 0644]
doc/src/sgml/ref/alter_table.sgml
doc/src/sgml/ref/comment.sgml
doc/src/sgml/ref/create_foreign_data_wrapper.sgml
doc/src/sgml/ref/create_foreign_table.sgml [new file with mode: 0644]
doc/src/sgml/ref/create_sequence.sgml
doc/src/sgml/ref/create_table.sgml
doc/src/sgml/ref/create_view.sgml
doc/src/sgml/ref/drop_foreign_table.sgml [new file with mode: 0644]
doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/psql-ref.sgml
doc/src/sgml/reference.sgml
src/backend/access/common/reloptions.c
src/backend/catalog/Makefile
src/backend/catalog/aclchk.c
src/backend/catalog/dependency.c
src/backend/catalog/heap.c
src/backend/catalog/information_schema.sql
src/backend/catalog/objectaddress.c
src/backend/commands/alter.c
src/backend/commands/analyze.c
src/backend/commands/comment.c
src/backend/commands/foreigncmds.c
src/backend/commands/lockcmds.c
src/backend/commands/seclabel.c
src/backend/commands/tablecmds.c
src/backend/commands/vacuum.c
src/backend/foreign/foreign.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/backend/tcop/utility.c
src/backend/utils/adt/acl.c
src/backend/utils/adt/pseudotypes.c
src/backend/utils/cache/syscache.c
src/backend/utils/init/miscinit.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/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_class.h
src/include/catalog/pg_foreign_data_wrapper.h
src/include/catalog/pg_foreign_table.h [new file with mode: 0644]
src/include/catalog/pg_proc.h
src/include/catalog/pg_type.h
src/include/commands/defrem.h
src/include/foreign/foreign.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/utils/acl.h
src/include/utils/builtins.h
src/include/utils/errcodes.h
src/include/utils/syscache.h
src/test/regress/expected/alter_table.out
src/test/regress/expected/foreign_data.out
src/test/regress/expected/sanity_check.out
src/test/regress/expected/type_sanity.out
src/test/regress/sql/foreign_data.sql
src/test/regress/sql/type_sanity.sql

index f341a7247d1ac59f6f5581b4a07ba8cb92db268c..822a31f08038d7e4cc8c981df8d93689e6cb8ee8 100644 (file)
@@ -119,6 +119,11 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("cannot get raw page from composite type \"%s\"",
                        RelationGetRelationName(rel))));
+   if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+       ereport(ERROR,
+               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                errmsg("cannot get raw page from foreign table \"%s\"",
+                       RelationGetRelationName(rel))));
 
    /*
     * Reject attempts to read non-local temporary relations; we would be
index 3a5d9c27b442b88961d8c5fb2fb4af3e60204d38..e5ddd87091036961e3e7a843288bdb0c024f72f4 100644 (file)
@@ -242,6 +242,9 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
        case RELKIND_COMPOSITE_TYPE:
            err = "composite type";
            break;
+       case RELKIND_FOREIGN_TABLE:
+           err = "foreign table";
+           break;
        default:
            err = "unknown";
            break;
index ef35fd9767ec7a25c23e613ed5f3765093470e3a..897885a1aae17032993062eac1c8823bd0c877dc 100644 (file)
       <entry>foreign server definitions</entry>
      </row>
 
+     <row>
+      <entry><link linkend="catalog-pg-foreign-table"><structname>pg_foreign_table</structname></link></entry>
+      <entry>additional foreign table information</entry>
+     </row>
+
      <row>
       <entry><link linkend="catalog-pg-index"><structname>pg_index</structname></link></entry>
       <entry>additional index information</entry>
       <entry>
        <literal>r</> = ordinary table, <literal>i</> = index,
        <literal>S</> = sequence, <literal>v</> = view, <literal>c</> =
-       composite type, <literal>t</> = TOAST
-       table
+       composite type, <literal>t</> = TOAST table,
+       <literal>f</> = foiregn table
       </entry>
      </row>
 
       </entry>
      </row>
 
+     <row>
+      <entry><structfield>fdwhandler</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>
+       References a handler function that is responsible for
+       supplying foreign-data wrapper routines.
+       Zero if no handler is provided.
+      </entry>
+     </row>
+
      <row>
       <entry><structfield>fdwacl</structfield></entry>
       <entry><type>aclitem[]</type></entry>
  </sect1>
 
 
+ <sect1 id="catalog-pg-foreign-table">
+  <title><structname>pg_foreign_table</structname></title>
+
+  <indexterm zone="catalog-pg-foreign-table">
+   <primary>pg_foreign_table</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_foreign_table</structname> contains part
+   of the information about foreign tables.
+   The rest is mostly in <structname>pg_class</structname>.
+  </para>
+
+  <table>
+   <title><structname>pg_foreign_table</> 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>ftrelid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+      <entry>The OID of the <structname>pg_class</> entry for this foreign table</entry>
+     </row>
+
+     <row>
+      <entry><structfield>ftserver</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 for this foreign table</entry>
+     </row>
+
+     <row>
+      <entry><structfield>srvoptions</structfield></entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>
+       Foreign table specific options, as <quote>keyword=value</> strings.
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+
  <sect1 id="catalog-pg-index">
   <title><structname>pg_index</structname></title>
 
index 9d30949aa77dc6ea5a0c03221765c3c9a94add5a..962b85b8e4bfaf444af51ca77bf5e7d6ebd5f5c4 100644 (file)
@@ -2384,6 +2384,132 @@ ORDER BY c.ordinal_position;
   </table>
  </sect1>
 
+ <sect1 id="infoschema-foreign-table-options">
+  <title><literal>foreign_table_options</literal></title>
+
+  <para>
+   The view <literal>foreign_table_options</literal> contains all the
+   options defined for foreign tables in the current database.  Only
+   those foreign tables are shown that the current user has access to
+   (by way of being the owner or having some privilege).
+  </para>
+
+  <table>
+   <title><literal>foreign_table_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_table_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that contains the foreign table (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_table_schema</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the schema that contains the foreign table</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_table_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign table</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 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-tables">
+  <title><literal>foreign_tables</literal></title>
+
+  <para>
+   The view <literal>foreign_tables</literal> contains all foreign
+   tables defined in the current database.  Only those foreign
+   tables are shown that the current user has access to (by way of
+   being the owner or having some privilege).
+  </para>
+
+  <table>
+   <title><literal>foreign_tables</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_table_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that the foreign table is defined in (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_table_schema</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the schema that contains the foreign table</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_table_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign table</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 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>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
  <sect1 id="infoschema-key-column-usage">
   <title><literal>key_column_usage</literal></title>
 
@@ -4730,8 +4856,9 @@ ORDER BY c.ordinal_position;
       <entry>
        Type of the table: <literal>BASE TABLE</literal> for a
        persistent base table (the normal table type),
-       <literal>VIEW</literal> for a view, or <literal>LOCAL
-       TEMPORARY</literal> for a temporary table
+       <literal>VIEW</literal> for a view, <literal>FOREIGN TABLE</literal>
+       for a foreign table, or
+       <literal>LOCAL TEMPORARY</literal> for a temporary table
       </entry>
      </row>
 
index a352a431419b8b36722f41a85a716d4f3a21a449..f40fa9dd8b26267d7910d090e0360d547b01b8c5 100644 (file)
@@ -12,6 +12,7 @@ Complete list of usable sgml source files in this directory.
 <!entity alterDefaultPrivileges system "alter_default_privileges.sgml">
 <!entity alterDomain        system "alter_domain.sgml">
 <!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml">
+<!entity alterForeignTable  system "alter_foreign_table.sgml">
 <!entity alterFunction      system "alter_function.sgml">
 <!entity alterGroup         system "alter_group.sgml">
 <!entity alterIndex         system "alter_index.sgml">
@@ -50,6 +51,7 @@ Complete list of usable sgml source files in this directory.
 <!entity createDatabase     system "create_database.sgml">
 <!entity createDomain       system "create_domain.sgml">
 <!entity createForeignDataWrapper system "create_foreign_data_wrapper.sgml">
+<!entity createForeignTable system "create_foreign_table.sgml">
 <!entity createFunction     system "create_function.sgml">
 <!entity createGroup        system "create_group.sgml">
 <!entity createIndex        system "create_index.sgml">
@@ -85,6 +87,7 @@ Complete list of usable sgml source files in this directory.
 <!entity dropDatabase       system "drop_database.sgml">
 <!entity dropDomain         system "drop_domain.sgml">
 <!entity dropForeignDataWrapper system "drop_foreign_data_wrapper.sgml">
+<!entity dropForeignTable   system "drop_foreign_table.sgml">
 <!entity dropFunction       system "drop_function.sgml">
 <!entity dropGroup          system "drop_group.sgml">
 <!entity dropIndex          system "drop_index.sgml">
index c27466fbef9aeacb952b5270d0b13f0c11800299..cc4f3636a25c983955f61c8c24da66d6ab0b42a0 100644 (file)
@@ -71,8 +71,8 @@ REVOKE [ GRANT OPTION FOR ]
    <command>ALTER DEFAULT PRIVILEGES</> allows you to set the privileges
    that will be applied to objects created in the future.  (It does not
    affect privileges assigned to already-existing objects.)  Currently,
-   only the privileges for tables (including views), sequences, and
-   functions can be altered.
+   only the privileges for tables (including views and foreign tables),
+   sequences, and functions can be altered.
   </para>
 
   <para>
index 4e9e8a2e28a323557c2da07ce1c50eda0f0f64c4..ead2c2ebcc1fc3530ac15c0743cb23b989c5db6f 100644 (file)
@@ -23,6 +23,7 @@ PostgreSQL documentation
 <synopsis>
 ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
     [ VALIDATOR <replaceable class="parameter">valfunction</replaceable> | NO VALIDATOR ]
+    [ HANDLER <replaceable class="parameter">handler</replaceable> | NO HANDLER ]
     [ 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>
@@ -85,6 +86,29 @@ ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWN
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>HANDLER <replaceable class="parameter">handler</replaceable></literal></term>
+    <listitem>
+     <para>
+      Specifies a new foreign-data wrapper handler function.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>NO HANDLER</literal></term>
+    <listitem>
+     <para>
+      This is used to specify that the foreign-data wrapper should no
+      longer have a handler function.
+     </para>
+     <para>
+      Note that foreign tables which uses a foreign-data wrapper with no 
+      handler can't be used in a SELECT statement.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
     <listitem>
@@ -127,8 +151,8 @@ ALTER FOREIGN DATA WRAPPER dbi VALIDATOR bob.myvalidator;
   <para>
    <command>ALTER FOREIGN DATA WRAPPER</command> conforms to ISO/IEC
    9075-9 (SQL/MED).  The standard does not specify the <literal>
-   VALIDATOR</literal> and <literal>OWNER TO</> variants of the
-   command.
+   VALIDATOR</literal>, <literal>HANDLER</> and <literal>OWNER TO</>
+   variants of the command.
   </para>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml
new file mode 100644 (file)
index 0000000..f9a2215
--- /dev/null
@@ -0,0 +1,516 @@
+<!--
+doc/src/sgml/rel/alter_foreign_table.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERFOREIGNTABLE">
+ <refmeta>
+  <refentrytitle>ALTER FOREIGN TABLE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER FOREIGN TABLE</refname>
+  <refpurpose>change the definition of a foreign table</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterforeigntable">
+  <primary>ALTER FOREIGN TABLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable> [ * ]
+    <replaceable class="PARAMETER">action</replaceable> [, ... ]
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable> [ * ]
+    RENAME [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> TO <replaceable class="PARAMETER">new_column</replaceable>
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
+    RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
+    SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+
+    ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+    DROP [ COLUMN ] [ IF EXISTS ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">type</replaceable>
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
+    ADD <replaceable class="PARAMETER">table_constraint</replaceable>
+    DROP CONSTRAINT [ IF EXISTS ]  <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
+    INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
+    NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
+    OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
+    OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER FOREIGN TABLE</command> changes the definition of an existing table.
+   There are several subforms:
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>ADD COLUMN</literal></term>
+    <listitem>
+     <para>
+      This form adds a new column to the foreign table, using the same syntax as
+      <xref linkend="SQL-CREATEFOREIGNTABLE">.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>DROP COLUMN [ IF EXISTS ]</literal></term>
+    <listitem>
+     <para>
+      This form drops a column from a foreign table.
+      Table constraints involving the column will be automatically
+      dropped as well.  You will need to say <literal>CASCADE</> if
+      anything outside the table depends on the column, for example,
+      views.
+      If <literal>IF EXISTS</literal> is specified and the column
+      does not exist, no error is thrown. In this case a notice
+      is issued instead.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET DATA TYPE</literal></term>
+    <listitem>
+     <para>
+      This form changes the type of a column of a foreign table.
+      Simple table constraints involving the column will be automatically
+      converted to use the new column type by reparsing the originally
+      supplied expression.  The optional <literal>USING</literal>
+      clause specifies how to compute the new column value from the old;
+      if omitted, the default conversion is the same as an assignment
+      cast from old data type to new.  A  <literal>USING</literal>
+      clause must be provided if there is no implicit or assignment
+      cast from old to new type.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET</literal>/<literal>DROP NOT NULL</literal></term>
+    <listitem>
+     <para>
+      These forms change whether a column is marked to allow null
+      values or to reject null values.  You can only use <literal>SET
+      NOT NULL</> when the column contains no null values.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable></literal></term>
+    <listitem>
+     <para>
+      This form adds a new constraint to a foreign table using the same syntax as
+      <xref linkend="SQL-CREATETABLE">.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>DROP CONSTRAINT [ IF EXISTS ]</literal></term>
+    <listitem>
+     <para>
+      This form drops the specified constraint on a foreign table.
+      If <literal>IF EXISTS</literal> is specified and the constraint
+      does not exist, no error is thrown. In this case a notice is issued instead.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term>
+    <listitem>
+     <para>
+      This form adds the target table as a new child of the specified parent
+      table.  Subsequently, queries against the parent will include records
+      of the target table.  To be added as a child, the target table must
+      already contain all the same columns as the parent (it could have
+      additional columns, too).  The columns must have matching data types,
+      and if they have <literal>NOT NULL</literal> constraints in the parent
+      then they must also have <literal>NOT NULL</literal> constraints in the
+      child.
+     </para>
+
+     <para>
+      There must also be matching child-table constraints for all
+      <literal>CHECK</literal> constraints of the parent.
+     </para>
+
+     <para>
+      The parent must not have OIDs because foreign table can't have OIDs.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term>
+    <listitem>
+     <para>
+      This form removes the target table from the list of children of the
+      specified parent table.
+      Queries against the parent table will no longer include records drawn
+      from the target table.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OWNER</literal></term>
+    <listitem>
+     <para>
+      This form changes the owner of the foreign table to the
+      specified user.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RENAME</literal></term>
+    <listitem>
+     <para>
+      The <literal>RENAME</literal> forms change the name of a foreign table
+      or the name of an individual column in
+      a foreign table. There is no effect on the stored data.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET SCHEMA</literal></term>
+    <listitem>
+     <para>
+      This form moves the foreign table into another schema.  Associated
+      constraints owned by table columns are moved as well.
+     </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 table or the column of the foreign table.
+      <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>
+  </para>
+
+  <para>
+   All the actions except <literal>RENAME</literal> and <literal>SET SCHEMA</>
+   can be combined into
+   a list of multiple alterations to apply in parallel.  For example, it
+   is possible to add several columns and/or alter the type of several
+   columns in a single command.  This is particularly useful with large
+   foreign tables, since only one pass over the table need be made.
+  </para>
+
+  <para>
+   You must own the table to use <command>ALTER FOREIGN TABLE</>.
+   To change the schema of a foreign table, you must also have
+   <literal>CREATE</literal> privilege on the new schema.
+   To add the table as a new child of a parent table, you must own the
+   parent table as well.
+   To alter the owner, you must also be a direct or indirect member of the new
+   owning role, and that role must have <literal>CREATE</literal> privilege on
+   the table's schema.  (These restrictions enforce that altering the owner
+   doesn't do anything you couldn't do by dropping and recreating the table.
+   However, a superuser can alter ownership of any table anyway.)
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+    <variablelist>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">name</replaceable></term>
+      <listitem>
+       <para>
+        The name (possibly schema-qualified) of an existing table to
+        alter. If <literal>ONLY</> is specified, only that table is
+        altered. If <literal>ONLY</> is not specified, the table and any
+        descendant foreign tables are altered.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">column</replaceable></term>
+      <listitem>
+       <para>
+        Name of a new or existing column.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">new_column</replaceable></term>
+      <listitem>
+       <para>
+        New name for an existing column.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">new_name</replaceable></term>
+      <listitem>
+       <para>
+        New name for the table.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">type</replaceable></term>
+      <listitem>
+       <para>
+        Data type of the new column, or new data type for an existing
+        column.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">table_constraint</replaceable></term>
+      <listitem>
+       <para>
+        New table constraint for the table.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">constraint_name</replaceable></term>
+      <listitem>
+       <para>
+        Name of an existing constraint to drop.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>CASCADE</literal></term>
+      <listitem>
+       <para>
+        Automatically drop objects that depend on the dropped column
+        or constraint (for example, views referencing the column).
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>RESTRICT</literal></term>
+      <listitem>
+       <para>
+        Refuse to drop the column or constraint if there are any dependent
+        objects. This is the default behavior.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">storage_parameter</replaceable></term>
+      <listitem>
+       <para>
+        The name of a foreign table storage parameter.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">value</replaceable></term>
+      <listitem>
+       <para>
+        The new value for a foreign table storage parameter.
+        This might be a number or a word depending on the parameter.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">parent_table</replaceable></term>
+      <listitem>
+       <para>
+        A parent table to associate or de-associate with this table.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">new_owner</replaceable></term>
+      <listitem>
+       <para>
+        The user name of the new owner of the table.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">new_schema</replaceable></term>
+      <listitem>
+       <para>
+        The name of the schema to which the table will be moved.
+       </para>
+      </listitem>
+     </varlistentry>
+
+    </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+   <para>
+    The key word <literal>COLUMN</literal> is noise and can be omitted.
+   </para>
+
+   <para>
+    Consistency with the foreign server is not checked even when a column is
+    added or removed with <literal>ADD COLUMN</literal> or
+    <literal>DROP COLUMN</literal>, a system <literal>oid</> column is added
+    or removed, a <literal>CHECK</> or <literal>NOT NULL</> constraint is
+    added, or column type is changed with <literal>ALTER TYPE</>.
+   </para>
+
+   <para>
+    Refer to <xref linkend="sql-createforeigntable"> for a further description of valid
+    parameters. <xref linkend="ddl"> has further information on
+    inheritance.
+   </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   To add a column of type <type>varchar</type> to a foreign table:
+<programlisting>
+ALTER FOREIGN TABLE distributors ADD COLUMN address varchar(30);
+</programlisting>
+  </para>
+
+  <para>
+   To drop a column from a foreign table:
+<programlisting>
+ALTER FOREIGN TABLE distributors DROP COLUMN address RESTRICT;
+</programlisting>
+  </para>
+
+  <para>
+   To change the types of two existing columns in one operation:
+<programlisting>
+ALTER FOREIGN TABLE distributors
+    ALTER COLUMN address TYPE varchar(80),
+    ALTER COLUMN name TYPE varchar(100);
+</programlisting>
+  </para>
+
+  <para>
+   To rename an existing column:
+<programlisting>
+ALTER FOREIGN TABLE distributors RENAME COLUMN address TO city;
+</programlisting>
+  </para>
+
+  <para>
+   To rename an existing table:
+<programlisting>
+ALTER FOREIGN TABLE distributors RENAME TO suppliers;
+</programlisting>
+  </para>
+
+  <para>
+   To add a not-null constraint to a column:
+<programlisting>
+ALTER FOREIGN TABLE distributors ALTER COLUMN street SET NOT NULL;
+</programlisting>
+   To remove a not-null constraint from a column:
+<programlisting>
+ALTER FOREIGN TABLE distributors ALTER COLUMN street DROP NOT NULL;
+</programlisting>
+  </para>
+
+  <para>
+   To chage options of a column of a foreign table:
+<programlisting>
+ALTER FOREIGN TABLE myschema.distributors ALTER COLUMN street OPTIONS (ADD opt1 'value', SET opt2, 'value2', DROP opt3 'value3');
+</programlisting>
+  </para>
+
+  <para>
+   To add a check constraint to a foreign table:
+<programlisting>
+ALTER FOREIGN TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
+</programlisting>
+  </para>
+
+  <para>
+   To remove a check constraint from a foreign table and all its children:
+<programlisting>
+ALTER FOREIGN TABLE distributors DROP CONSTRAINT zipchk;
+</programlisting>
+  </para>
+
+  <para>
+   To remove a check constraint from a foreign table only:
+<programlisting>
+ALTER FOREIGN TABLE ONLY distributors DROP CONSTRAINT zipchk;
+</programlisting>
+  </para>
+
+  <para>
+   To move a foreign table to a different schema:
+<programlisting>
+ALTER FOREIGN TABLE myschema.distributors SET SCHEMA yourschema;
+</programlisting>
+  </para>
+
+  <para>
+   To chage options of a foreign table:
+<programlisting>
+ALTER FOREIGN TABLE myschema.distributors OPTIONS (ADD opt1 'value', SET opt2, 'value2', DROP opt3 'value3');
+</programlisting>
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   The forms <literal>ADD</literal>, <literal>DROP</>,
+   and <literal>SET DATA TYPE</literal>
+   conform with the SQL standard.  The other forms are
+   <productname>PostgreSQL</productname> extensions of the SQL standard.
+   Also, the ability to specify more than one manipulation in a single
+   <command>ALTER FOREIGN TABLE</> command is an extension.
+  </para>
+
+  <para>
+   <command>ALTER FOREIGN TABLE DROP COLUMN</> can be used to drop the only
+   column of a foreign table, leaving a zero-column table.  This is an
+   extension of SQL, which disallows zero-column foreign tables.
+  </para>
+ </refsect1>
+</refentry>
index 784feaef54887a44e7da04ad68e0e8ee1f5f7ad7..7a1da1dbd622841adc32a3c2c1ebf8228d9a9125 100644 (file)
@@ -319,6 +319,12 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
       that would add a normal column that happened to be named
       <literal>oid</>, not a system column.
      </para>
+
+     <para>
+      If the target table has been inherited by any foreign table, you can't
+      add <literal>oid</literal> system column because foreign table can't have
+      <literal>oid</literal>.
+     </para>
     </listitem>
    </varlistentry>
 
index d81fd726414d8ef6bc36f580941961379b1762a4..594e9b870fdc8da763883fb390e3c525eac93665 100644 (file)
@@ -31,6 +31,7 @@ COMMENT ON
   CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
   DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
   DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
+  FOREIGN TABLE <replaceable class="PARAMETER">object_name</replaceable> |
   FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) |
   INDEX <replaceable class="PARAMETER">object_name</replaceable> |
   LARGE OBJECT <replaceable class="PARAMETER">large_object_oid</replaceable> |
@@ -247,6 +248,7 @@ COMMENT ON COLUMN my_table.my_column IS 'Employee ID number';
 COMMENT ON CONVERSION my_conv IS 'Conversion to UTF8';
 COMMENT ON DATABASE my_database IS 'Development Database';
 COMMENT ON DOMAIN my_domain IS 'Email Address Domain';
+COMMENT ON FOREIGN TABLE my_schema.my_foreign_table IS 'Employee Information in other database';
 COMMENT ON FUNCTION my_function (timestamp) IS 'Returns Roman Numeral';
 COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee ID';
 COMMENT ON LANGUAGE plpython IS 'Python support for stored procedures';
index f626d56665b25722987107b8c6effb6ca0657de1..e61a725de34c8981263f4213ca6a6553e83c2254 100644 (file)
@@ -23,6 +23,7 @@ PostgreSQL documentation
 <synopsis>
 CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
     [ VALIDATOR <replaceable class="parameter">valfunction</replaceable> | NO VALIDATOR ]
+    [ HANDLER <replaceable class="parameter">handler</replaceable> | NO HANDLER ]
     [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
@@ -81,6 +82,19 @@ CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>HANDLER <replaceable class="parameter">handler</replaceable></literal></term>
+    <listitem>
+     <para>
+      <replaceable class="parameter">handler</replaceable> is the
+      name of a previously registered function that will be called to
+      retrieve a set of functions for foreign tables.
+      The validator function must take no arguments.
+      The return type must be <type>fdw_handler</type>.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] )</literal></term>
     <listitem>
@@ -151,8 +165,8 @@ CREATE FOREIGN DATA WRAPPER mywrapper
 
   <para>
    <command>CREATE FOREIGN DATA WRAPPER</command> conforms to ISO/IEC
-   9075-9 (SQL/MED), with the exception that
-   the <literal>VALIDATOR</literal> clause is an extension and the
+   9075-9 (SQL/MED), with the exception that the <literal>VALIDATOR</literal>
+   and <literal>HANDLER</literal> clauses are extensions and the
    clauses <literal>LIBRARY</literal> and <literal>LANGUAGE</literal>
    are not yet implemented in PostgreSQL.
   </para>
diff --git a/doc/src/sgml/ref/create_foreign_table.sgml b/doc/src/sgml/ref/create_foreign_table.sgml
new file mode 100644 (file)
index 0000000..6413af2
--- /dev/null
@@ -0,0 +1,426 @@
+<!-- doc/src/sgml/ref/create_foreign_table.sgml -->
+
+<refentry id="SQL-CREATEFOREIGNTABLE">
+ <refmeta>
+  <refentrytitle>CREATE FOREIGN TABLE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE FOREIGN TABLE</refname>
+  <refpurpose>define a new foreign table</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-createforeigntable">
+  <primary>CREATE FOREIGN TABLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE FOREIGN TABLE <replaceable class="PARAMETER">table_name</replaceable> ( [
+  { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+    | <replaceable>table_constraint</replaceable>
+    | LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
+    [, ... ]
+] )
+[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
+  SERVER <replaceable class="parameter">server_name</replaceable>
+[ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
+
+<phrase>where <replaceable class="PARAMETER">column_constraint</replaceable> is:</phrase>
+
+[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
+{ NOT NULL |
+  NULL |
+  CHECK ( <replaceable class="PARAMETER">expression</replaceable> )
+}
+
+<phrase>and <replaceable class="PARAMETER">table_constraint</replaceable> is:</phrase>
+
+[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
+CHECK ( <replaceable class="PARAMETER">expression</replaceable> )
+
+<phrase>and <replaceable class="PARAMETER">like_option</replaceable> is:</phrase>
+
+{ INCLUDING | EXCLUDING } { CONSTRAINTS | COMMENTS | ALL }
+</synopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1 id="SQL-CREATEFOREIGNTABLE-description">
+  <title>Description</title>
+
+  <para>
+   <command>CREATE FOREIGN TABLE</command> will create a new foreign table
+   in the current database. The table will be owned by the user issuing the
+   command.
+  </para>
+
+  <para>
+   If a schema name is given (for example, <literal>CREATE FOREIGN TABLE
+   myschema.mytable ...</>) then the table is created in the specified
+   schema.  Otherwise it is created in the current schema.
+   The name of the foreign table must be
+   distinct from the name of any other foreign table, table, sequence, index,
+   or view in the same schema.
+  </para>
+
+  <para>
+   <command>CREATE FOREIGN TABLE</command> also automatically creates a data
+   type that represents the composite type corresponding to one row of
+   the foreign table.  Therefore, foreign tables cannot have the same
+   name as any existing data type in the same schema.
+  </para>
+
+  <para>
+   The optional constraint clauses specify constraints (tests) that
+   retrieved rows must satisfy for an select operation
+   to succeed.  A constraint is an SQL object that helps define the
+   set of valid values in the table in various ways.
+  </para>
+
+  <para>
+   There are two ways to define constraints: table constraints and
+   column constraints.  A column constraint is defined as part of a
+   column definition.  A table constraint definition is not tied to a
+   particular column, and it can encompass more than one column.
+   Every column constraint can also be written as a table constraint;
+   a column constraint is only a notational convenience for use when the
+   constraint only affects one column.
+  </para>
+
+  <para>
+  To create a foreign table, the foreign-data wrapper of the foreign
+  server must have handler function.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">table_name</replaceable></term>
+    <listitem>
+     <para>
+      The name (optionally schema-qualified) of the table to be created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">column_name</replaceable></term>
+    <listitem>
+     <para>
+      The name of a column to be created in the new table.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">data_type</replaceable></term>
+    <listitem>
+     <para>
+      The data type of the column. This can include array
+      specifiers. For more information on the data types supported by
+      <productname>PostgreSQL</productname>, refer to <xref
+      linkend="datatype">.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> = '<replaceable class="PARAMETER">value</replaceable>' [, ...] )</literal></term>
+    <listitem>
+     <para>
+      This clause specified options for the new foreign table, or the
+      column of the new foreign table.
+      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>
+
+   <varlistentry>
+    <term><literal>INHERITS ( <replaceable>parent_table</replaceable> [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      The optional <literal>INHERITS</> clause specifies a list of
+      tables from which the new table automatically inherits all
+      columns.
+     </para>
+
+     <para>
+      Use of <literal>INHERITS</> creates a persistent relationship
+      between the new child table and its parent table(s).  Schema
+      modifications to the parent(s) normally propagate to children
+      as well, and by default the data of the child table is included in
+      scans of the parent(s).
+     </para>
+
+     <para>
+      If the same column name exists in more than one parent
+      table, an error is reported unless the data types of the columns
+      match in each of the parent tables.  If there is no conflict,
+      then the duplicate columns are merged to form a single column in
+      the new table.  If the column name list of the new table
+      contains a column name that is also inherited, the data type must
+      likewise match the inherited column(s), and the column
+      definitions are merged into one.  If the
+      new table explicitly specifies a default value for the column,
+      this default overrides any defaults from inherited declarations
+      of the column.  Otherwise, any parents that specify default
+      values for the column must all specify the same default, or an
+      error will be reported.
+     </para>
+
+     <para>
+      <literal>CHECK</> constraints are merged in essentially the same way as
+      columns: if multiple parent tables and/or the new table definition
+      contain identically-named <literal>CHECK</> constraints, these
+      constraints must all have the same check expression, or an error will be
+      reported.  Constraints having the same name and expression will
+      be merged into one copy.  Notice that an unnamed <literal>CHECK</>
+      constraint in the new table will never be merged, since a unique name
+      will always be chosen for it.
+     </para>
+
+     <para>
+      The parent must not have OIDs because foreign table can't have OIDs.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ]</literal></term>
+    <listitem>
+     <para>
+      The <literal>LIKE</literal> clause specifies a table from which
+      the new table automatically copies all column names, their data types,
+      and their not-null constraints.
+     </para>
+     <para>
+      Unlike <literal>INHERITS</literal>, the new table and original table
+      are completely decoupled after creation is complete.  Changes to the
+      original table will not be applied to the new table, and it is not
+      possible to include data of the new table in scans of the original
+      table.
+     </para>
+     <para>
+      Not-null constraints are always copied to the new table.
+      <literal>CHECK</literal> constraints will only be copied if
+      <literal>INCLUDING CONSTRAINTS</literal> is specified; other types of
+      constraints will never be copied. Also, no distinction is made between
+      column constraints and table constraints &mdash; when constraints are
+      requested, all check constraints are copied.
+     </para>
+     <para>
+      Comments for the copied columns and constraints
+      will only be copied if <literal>INCLUDING COMMENTS</literal>
+      is specified. The default behavior is to exclude comments, resulting in
+      the copied columns and constraints in the new table having no comments.
+     </para>
+     <para>
+      <literal>INCLUDING ALL</literal> is an abbreviated form of
+      <literal>INCLUDING CONSTRAINTS INCLUDING COMMENTS</literal>.
+     </para>
+     <para>
+      Note also that unlike <literal>INHERITS</literal>, columns and
+      constraints copied by <literal>LIKE</> are not merged with similarly
+      named columns and constraints.
+      If the same name is specified explicitly or in another
+      <literal>LIKE</literal> clause, an error is signalled.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable></literal></term>
+    <listitem>
+     <para>
+      An optional name for a column or table constraint.  If the
+      constraint is violated, the constraint name is present in error messages,
+      so constraint names like <literal>col must be positive</> can be used
+      to communicate helpful constraint information to client applications.
+      (Double-quotes are needed to specify constraint names that contain spaces.)
+      If a constraint name is not specified, the system generates a name.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>NOT NULL</></term>
+    <listitem>
+     <para>
+      The column is not allowed to contain null values.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>NULL</></term>
+    <listitem>
+     <para>
+      The column is allowed to contain null values. This is the default.
+     </para>
+
+     <para>
+      This clause is only provided for compatibility with
+      non-standard SQL databases.  Its use is discouraged in new
+      applications.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CHECK ( <replaceable class="PARAMETER">expression</replaceable> )</literal></term>
+    <listitem>
+     <para>
+      The <literal>CHECK</> clause specifies an expression producing a
+      Boolean result which new or updated rows must satisfy for an
+      insert or update operation to succeed.  Expressions evaluating
+      to TRUE or UNKNOWN succeed.  Should any row of an insert or
+      update operation produce a FALSE result an error exception is
+      raised and the insert or update does not alter the database.  A
+      check constraint specified as a column constraint should
+      reference that column's value only, while an expression
+      appearing in a table constraint can reference multiple columns.
+     </para>
+
+     <para>
+      Currently, <literal>CHECK</literal> expressions cannot contain
+      subqueries nor refer to variables other than columns of the
+      current row.
+     </para>
+    </listitem>
+   </varlistentry>
+
+  </variablelist>
+
+ </refsect1>
+
+
+ <refsect1 id="SQL-CREATEFOREIGNTABLE-examples">
+  <title>Examples</title>
+
+  <para>
+   Create foreign table <structname>films</> with <structname>film_server</>:
+
+<programlisting>
+CREATE FOREIGN TABLE films (
+    code        char(5) NOT NULL,
+    title       varchar(40) NOT NULL,
+    did         integer NOT NULL,
+    date_prod   date,
+    kind        varchar(10),
+    len         interval hour to minute
+)
+SERVER film_server;
+</programlisting>
+  </para>
+
+  <para>
+   Define a check column constraint:
+
+<programlisting>
+CREATE FOREIGN TABLE distributors (
+    did     integer CHECK (did &gt; 100),
+    name    varchar(40)
+)
+SERVER distributor_server;
+</programlisting>
+  </para>
+
+  <para>
+   Define a check table constraint:
+
+<programlisting>
+CREATE FOREIGN TABLE distributors (
+    did     integer,
+    name    varchar(40)
+    CONSTRAINT con1 CHECK (did &gt; 100 AND name &lt;&gt; '')
+)
+SERVER distributor_server;
+</programlisting>
+  </para>
+
+ </refsect1>
+
+ <refsect1 id="SQL-CREATEFOREIGNTABLE-compatibility">
+  <title id="SQL-CREATEFOREIGNTABLE-compatibility-title">Compatibility</title>
+
+  <para>
+   The <command>CREATE FOREIGN TABLE</command> command conforms to the
+   <acronym>SQL</acronym> standard, with exceptions listed below.
+  </para>
+
+  <refsect2>
+   <title>Column Check Constraints</title>
+
+   <para>
+    The SQL standard says that <literal>CHECK</> column constraints
+    can only refer to the column they apply to; only <literal>CHECK</>
+    table constraints can refer to multiple columns.
+    <productname>PostgreSQL</productname> does not enforce this
+    restriction; it treats column and table check constraints alike.
+   </para>
+  </refsect2>
+
+  <refsect2>
+   <title><literal>NULL</literal> <quote>Constraint</quote></title>
+
+   <para>
+    The <literal>NULL</> <quote>constraint</quote> (actually a
+    non-constraint) is a <productname>PostgreSQL</productname>
+    extension to the SQL standard that is included for compatibility with some
+    other database systems (and for symmetry with the <literal>NOT
+    NULL</literal> constraint).  Since it is the default for any
+    column, its presence is simply noise.
+   </para>
+  </refsect2>
+
+  <refsect2>
+   <title>Inheritance</title>
+
+   <para>
+    Multiple inheritance via the <literal>INHERITS</literal> clause is
+    a <productname>PostgreSQL</productname> language extension.
+    SQL:1999 and later define single inheritance using a
+    different syntax and different semantics.  SQL:1999-style
+    inheritance is not yet supported by
+    <productname>PostgreSQL</productname>.
+   </para>
+  </refsect2>
+
+  <refsect2>
+   <title>Zero-column tables</title>
+
+   <para>
+    <productname>PostgreSQL</productname> allows a table of no columns
+    to be created (for example, <literal>CREATE FOREIGN TABLE foo();</>).  This
+    is an extension from the SQL standard, which does not allow zero-column
+    tables.  Zero-column tables are not in themselves very useful, but
+    disallowing them creates odd special cases for <command>ALTER TABLE
+    DROP COLUMN</>, so it seems cleaner to ignore this spec restriction.
+   </para>
+  </refsect2>
+
+ </refsect1>
+
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alterforeigntable"></member>
+   <member><xref linkend="sql-dropforeigntable"></member>
+   <member><xref linkend="sql-createtable"></member>
+   <member><xref linkend="sql-createtype"></member>
+   <member><xref linkend="sql-createserver"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
index 7f94d24865d7c019253f33e93f433fcf6b65bc3a..f36b616db0ebe989709e7a968f82a6aa64264a98 100644 (file)
@@ -45,7 +45,7 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE <replaceable class="parameter">name</replac
    Temporary sequences exist in a special schema, so a schema name cannot be
    given when creating a temporary sequence.
    The sequence name must be distinct from the name of any other sequence,
-   table, index, or view in the same schema.
+   table, index, view, or foreign table in the same schema.
   </para>
 
   <para>
index 8635e80faf3f2b9492510ab34338dc109e8ad0f7..4314fb13ecde58a2e0d02c2fe492cf246174c2c2 100644 (file)
@@ -96,8 +96,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE [ IF NOT EXISTS ] <repl
    schema.  Otherwise it is created in the current schema.  Temporary
    tables exist in a special schema, so a schema name cannot be given
    when creating a temporary table.  The name of the table must be
-   distinct from the name of any other table, sequence, index, or view
-   in the same schema.
+   distinct from the name of any other table, sequence, index, view,
+   or foreign table in the same schema.
   </para>
 
   <para>
index 6676383ab078fbe095766da61b0e8a92e3c2d86a..dd1550781115ee9770c9e238a52faab97e6749d7 100644 (file)
@@ -50,7 +50,7 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
    schema.  Otherwise it is created in the current schema.  Temporary
    views exist in a special schema, so a schema name cannot be given
    when creating a temporary view. The name of the view must be
-   distinct from the name of any other view, table, sequence, or index
+   distinct from the name of any other view, table, sequence, index or foreign table
    in the same schema.
   </para>
  </refsect1>
diff --git a/doc/src/sgml/ref/drop_foreign_table.sgml b/doc/src/sgml/ref/drop_foreign_table.sgml
new file mode 100644 (file)
index 0000000..f33442c
--- /dev/null
@@ -0,0 +1,120 @@
+<!-- doc/src/sggml/ref/drop_foreign_table.sgml -->
+
+<refentry id="SQL-DROPFOREIGNTABLE">
+ <refmeta>
+  <refentrytitle>DROP FOREIGN TABLE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP FOREIGN TABLE</refname>
+  <refpurpose>remove a foreign table</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-dropforeigntable">
+  <primary>DROP FOREIGN TABLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP FOREIGN TABLE</command> removes foreign tables from the database.
+   Only its owner can drop a foreign table.
+  </para>
+
+  <para>
+   <command>DROP FOREIGN TABLE</command> always removes any
+   constraints that exist for the target table.
+   However, to drop a foreign table that is referenced by a view,
+   <literal>CASCADE</> must be
+   specified. (<literal>CASCADE</> will remove a dependent view entirely,
+  </para>
+ </refsect1>
+  
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if theforeign table 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 (optionally schema-qualified) of theforeign table to drop.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CASCADE</literal></term>
+    <listitem>
+     <para>
+      Automatically drop objects that depend on theforeign table (such as
+      views).
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+    <listitem>
+     <para>
+      Refuse to drop theforeign table if any objects depend on it.  This is
+      the default.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   To destroy two foreign tables, <literal>films</literal> and 
+   <literal>distributors</literal>:
+
+<programlisting>
+DROP FOREIGN TABLE films, distributors;
+</programlisting>
+  </para>
+ </refsect1>
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   This command conforms to the ISO/IEC 9075-9 (SQL/MED), except that the
+   standard only allows one foreign table to be dropped per command, and apart
+   from the <literal>IF EXISTS</> option, which is a <productname>PostgreSQL</>
+   extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alterforeigntable"></member>
+   <member><xref linkend="sql-createforeigntable"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
index 8242b536d7319f8c2fac4f1310724cc0e0d6d087..8a1823198d57c1e7365c2897a8f7c96afe72d670 100644 (file)
@@ -408,9 +408,9 @@ PostgreSQL documentation
       <term><option>--table=<replaceable class="parameter">table</replaceable></option></term>
       <listitem>
        <para>
-        Dump only tables (or views or sequences) matching <replaceable
-        class="parameter">table</replaceable>.  Multiple tables can be
-        selected by writing multiple <option>-t</> switches.  Also, the
+        Dump only tables (or views or sequences or foreign tables) matching
+        <replaceable class="parameter">table</replaceable>.  Multiple tables
+        can be selected by writing multiple <option>-t</> switches.  Also, the
         <replaceable class="parameter">table</replaceable> parameter is
         interpreted as a pattern according to the same rules used by
         <application>psql</>'s <literal>\d</> commands (see <xref
index d44fc56b37e1c11cfcc659a27812cc35b7a9a160..f243513caccd870573a9e91d21ad6dcaeda95682 100644 (file)
@@ -867,12 +867,14 @@ testdb=&gt;
 
         <listitem>
         <para>
-        For each relation (table, view, index, or sequence) matching the
+        For each relation (table, view, index, sequence or foreign table)
+        matching the
         <replaceable class="parameter">pattern</replaceable>, show all
         columns, their types, the tablespace (if not the default) and any
         special attributes such as <literal>NOT NULL</literal> or defaults.
         Associated indexes, constraints, rules, and triggers are
-        also shown.
+        also shown.  If the relation was as foreign table, associated foreign
+        server is shown too.
         (<quote>Matching the pattern</> is defined in
         <xref linkend="APP-PSQL-patterns" endterm="APP-PSQL-patterns-title">
         below.)
@@ -882,7 +884,8 @@ testdb=&gt;
         The command form <literal>\d+</literal> is identical, except that
         more information is displayed: any comments associated with the
         columns of the table are shown, as is the presence of OIDs in the
-        table, and the view definition if the relation is a view.
+        table, the view definition if the relation is a view, and the generic
+        options if the relation is a foreign table.
         </para>
 
         <para>
@@ -895,9 +898,9 @@ testdb=&gt;
         <para>
         If <command>\d</command> is used without a
         <replaceable class="parameter">pattern</replaceable> argument, it is
-        equivalent to <command>\dtvs</command> which will show a list of
-        all visible tables, views, and sequences. This is purely a convenience
-        measure.
+        equivalent to <command>\dtvsE</command> which will show a list of
+        all visible tables, views, sequences and foreign tables.
+        This is purely a convenience measure.
         </para>
         </note>
         </listitem>
@@ -1034,6 +1037,20 @@ testdb=&gt;
       </varlistentry>
 
 
+      <varlistentry>
+        <term><literal>\det[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists foreign tables (mnemonic: <quote>external tables</quote>).
+        If <replaceable class="parameter">pattern</replaceable> is
+        specified, only entries whose table name or schema name matches
+        the pattern are listed.  If the form <literal>\det+</literal>
+        is used, combined generic options is shown additionally.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\des[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
@@ -1197,13 +1214,15 @@ testdb=&gt;
         <term><literal>\ds[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <term><literal>\dt[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <term><literal>\dv[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\dE[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
 
         <listitem>
         <para>
         In this group of commands, the letters
         <literal>i</literal>, <literal>s</literal>,
         <literal>t</literal>, and <literal>v</literal>
-        stand for index, sequence, table, and view, respectively.
+        stand for index, sequence, table, view, and foreign table,
+        respectively.
         You can specify any or all of
         these letters, in any order, to obtain a listing of objects
         of these types.  For example, <literal>\dit</> lists indexes
index 13de002792538595d0a7c23a9b79f19144dc328f..84babf61c00c70d5b6a5f19c65fd9956dc1cb821 100644 (file)
@@ -40,6 +40,7 @@
    &alterDefaultPrivileges;
    &alterDomain;
    &alterForeignDataWrapper;
+   &alterForeignTable;
    &alterFunction;
    &alterGroup;
    &alterIndex;
@@ -78,6 +79,7 @@
    &createDatabase;
    &createDomain;
    &createForeignDataWrapper;
+   &createForeignTable;
    &createFunction;
    &createGroup;
    &createIndex;
    &dropDatabase;
    &dropDomain;
    &dropForeignDataWrapper;
+   &dropForeignTable;
    &dropFunction;
    &dropGroup;
    &dropIndex;
index 438e8b007acd06653447db025845a718c3a573f1..0e8079390ffd07afd1272d2a64fb6c983898663d 100644 (file)
@@ -777,6 +777,7 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
        case RELKIND_RELATION:
        case RELKIND_TOASTVALUE:
        case RELKIND_UNCATALOGED:
+       case RELKIND_FOREIGN_TABLE:
            options = heap_reloptions(classForm->relkind, datum, false);
            break;
        case RELKIND_INDEX:
@@ -1173,6 +1174,12 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
            return (bytea *) rdopts;
        case RELKIND_RELATION:
            return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
+       case RELKIND_FOREIGN_TABLE:
+           if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("foreign table does not support storage options")));
+           return NULL;
        default:
            /* sequences, composite types and views are not supported */
            return NULL;
index 6a47f398ed85abc3384961ace63550df354aa507..17f4cc6cfc979fe3820a57a666908fc27f6e1ea5 100644 (file)
@@ -38,6 +38,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
    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 \
+   pg_foreign_table.h \
    pg_default_acl.h pg_seclabel.h \
    toasting.h indexing.h \
     )
index fefa335e0982d9fa9839aba406a54279bce5a473..b1a85bab061de0043c01160251ceb1b9c4e2474a 100644 (file)
@@ -272,6 +272,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
        case ACL_KIND_FOREIGN_SERVER:
            whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
            break;
+       case ACL_KIND_FOREIGN_TABLE:
+           whole_mask = ACL_ALL_RIGHTS_FOREIGN_TABLE;
+           break;
        default:
            elog(ERROR, "unrecognized object kind: %d", objkind);
            /* not reached, but keep compiler quiet */
@@ -475,6 +478,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
            all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
            errormsg = gettext_noop("invalid privilege type %s for foreign server");
            break;
+       case ACL_OBJECT_FOREIGN_TABLE:
+           all_privileges = ACL_ALL_RIGHTS_FOREIGN_TABLE;
+           errormsg = gettext_noop("invalid privilege type %s for foreign table");
+           break;
        default:
            elog(ERROR, "unrecognized GrantStmt.objtype: %d",
                 (int) stmt->objtype);
@@ -545,6 +552,7 @@ ExecGrantStmt_oids(InternalGrant *istmt)
    {
        case ACL_OBJECT_RELATION:
        case ACL_OBJECT_SEQUENCE:
+       case ACL_OBJECT_FOREIGN_TABLE:
            ExecGrant_Relation(istmt);
            break;
        case ACL_OBJECT_DATABASE:
@@ -594,6 +602,7 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
    {
        case ACL_OBJECT_RELATION:
        case ACL_OBJECT_SEQUENCE:
+       case ACL_OBJECT_FOREIGN_TABLE:
            foreach(cell, objnames)
            {
                RangeVar   *relvar = (RangeVar *) lfirst(cell);
@@ -718,11 +727,13 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
        switch (objtype)
        {
            case ACL_OBJECT_RELATION:
-               /* Process both regular tables and views */
+               /* Process both regular tables, views and foreign tables */
                objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
                objects = list_concat(objects, objs);
                objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
                objects = list_concat(objects, objs);
+               objs = getRelationsInNamespace(namespaceId, RELKIND_FOREIGN_TABLE);
+               objects = list_concat(objects, objs);
                break;
            case ACL_OBJECT_SEQUENCE:
                objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
@@ -1689,13 +1700,29 @@ ExecGrant_Relation(InternalGrant *istmt)
                     errmsg("\"%s\" is not a sequence",
                            NameStr(pg_class_tuple->relname))));
 
+       /* Used GRANT FOREIGN TABLE on a non-foreign-table? */
+       if (istmt->objtype == ACL_OBJECT_FOREIGN_TABLE &&
+           pg_class_tuple->relkind != RELKIND_FOREIGN_TABLE)
+           ereport(ERROR,
+                   (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                   errmsg("\"%s\" is not a foreign table",
+                           NameStr(pg_class_tuple->relname))));
+
        /* Adjust the default permissions based on whether it is a sequence */
        if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
        {
-           if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
-               this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
-           else
-               this_privileges = ACL_ALL_RIGHTS_RELATION;
+           switch (pg_class_tuple->relkind)
+           {
+               case RELKIND_SEQUENCE:
+                   this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+                   break;
+               case RELKIND_FOREIGN_TABLE:
+                   this_privileges = ACL_ALL_RIGHTS_FOREIGN_TABLE;
+                   break;
+               default:
+                   this_privileges = ACL_ALL_RIGHTS_RELATION;
+                   break;
+           }
        }
        else
            this_privileges = istmt->privileges;
@@ -1729,6 +1756,16 @@ ExecGrant_Relation(InternalGrant *istmt)
                    this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
                }
            }
+           else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE)
+           {
+               if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_FOREIGN_TABLE))
+               {
+                   ereport(ERROR,
+                           (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+                            errmsg("foreign table \"%s\" only supports SELECT privileges",
+                                   NameStr(pg_class_tuple->relname))));
+               }
+           }
            else
            {
                if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
@@ -1781,9 +1818,18 @@ ExecGrant_Relation(InternalGrant *istmt)
                                   &isNull);
        if (isNull)
        {
-           old_acl = acldefault(pg_class_tuple->relkind == RELKIND_SEQUENCE ?
-                                ACL_OBJECT_SEQUENCE : ACL_OBJECT_RELATION,
-                                ownerId);
+           switch (pg_class_tuple->relkind)
+           {
+               case RELKIND_SEQUENCE:
+                   old_acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
+                   break;
+               case RELKIND_FOREIGN_TABLE:
+                   old_acl = acldefault(ACL_OBJECT_FOREIGN_TABLE, ownerId);
+                   break;
+               default:
+                   old_acl = acldefault(ACL_OBJECT_RELATION, ownerId);
+                   break;
+           }
            /* There are no old member roles according to the catalogs */
            noldmembers = 0;
            oldmembers = NULL;
@@ -1812,12 +1858,26 @@ ExecGrant_Relation(InternalGrant *istmt)
            bool        replaces[Natts_pg_class];
            int         nnewmembers;
            Oid        *newmembers;
+           AclObjectKind aclkind;
 
            /* Determine ID to do the grant as, and available grant options */
            select_best_grantor(GetUserId(), this_privileges,
                                old_acl, ownerId,
                                &grantorId, &avail_goptions);
 
+           switch (pg_class_tuple->relkind)
+           {
+               case RELKIND_SEQUENCE:
+                   aclkind = ACL_KIND_SEQUENCE;
+                   break;
+               case RELKIND_FOREIGN_TABLE:
+                   aclkind = ACL_KIND_FOREIGN_TABLE;
+                   break;
+               default:
+                   aclkind = ACL_KIND_CLASS;
+                   break;
+           }
+
            /*
             * Restrict the privileges to what we can actually grant, and emit
             * the standards-mandated warning and error messages.
@@ -1825,9 +1885,7 @@ ExecGrant_Relation(InternalGrant *istmt)
            this_privileges =
                restrict_and_check_grant(istmt->is_grant, avail_goptions,
                                         istmt->all_privs, this_privileges,
-                                        relOid, grantorId,
-                                 pg_class_tuple->relkind == RELKIND_SEQUENCE
-                                        ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS,
+                                        relOid, grantorId, aclkind,
                                         NameStr(pg_class_tuple->relname),
                                         0, NULL);
 
@@ -1909,6 +1967,16 @@ ExecGrant_Relation(InternalGrant *istmt)
 
                this_privileges &= (AclMode) ACL_SELECT;
            }
+           else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE &&
+               this_privileges & ~((AclMode) ACL_SELECT))
+           {
+               /* Foreign tables have the same restriction with sequences. */
+               ereport(WARNING,
+                   (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+                    errmsg("foreign table \"%s\" only supports SELECT column privileges",
+                           NameStr(pg_class_tuple->relname))));
+               this_privileges &= (AclMode) ACL_SELECT;
+           }
 
            expand_col_privileges(col_privs->cols, relOid,
                                  this_privileges,
@@ -3080,7 +3148,9 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
    /* ACL_KIND_FDW */
    gettext_noop("permission denied for foreign-data wrapper %s"),
    /* ACL_KIND_FOREIGN_SERVER */
-   gettext_noop("permission denied for foreign server %s")
+   gettext_noop("permission denied for foreign server %s"),
+   /* ACL_KIND_FOREIGN_TABLE */
+   gettext_noop("permission denied for foreign table %s"),
 };
 
 static const char *const not_owner_msg[MAX_ACL_KIND] =
@@ -3120,7 +3190,9 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
    /* 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")
+   gettext_noop("must be owner of foreign server %s"),
+   /* ACL_KIND_FOREIGN_TABLE */
+   gettext_noop("must be owner of foreign table %s"),
 };
 
 
@@ -3410,9 +3482,18 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
    if (isNull)
    {
        /* No ACL, so build default ACL */
-       acl = acldefault(classForm->relkind == RELKIND_SEQUENCE ?
-                        ACL_OBJECT_SEQUENCE : ACL_OBJECT_RELATION,
-                        ownerId);
+       switch (classForm->relkind)
+       {
+           case RELKIND_SEQUENCE:
+               acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
+               break;
+           case RELKIND_FOREIGN_TABLE:
+               acl = acldefault(ACL_OBJECT_FOREIGN_TABLE, ownerId);
+               break;
+           default:
+               acl = acldefault(ACL_OBJECT_RELATION, ownerId);
+               break;
+       }
        aclDatum = (Datum) 0;
    }
    else
index a912971abae9a26e049a8a1d79664f737d95909b..461cb79c0ddf87d15df7ef05f8bf3a226d730ab6 100644 (file)
@@ -36,6 +36,7 @@
 #include "catalog/pg_depend.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_namespace.h"
@@ -152,6 +153,7 @@ static const Oid object_classes[MAX_OCLASS] = {
    ForeignDataWrapperRelationId,       /* OCLASS_FDW */
    ForeignServerRelationId,    /* OCLASS_FOREIGN_SERVER */
    UserMappingRelationId,      /* OCLASS_USER_MAPPING */
+   ForeignTableRelationId,     /* OCLASS_FOREIGN_TABLE */
    DefaultAclRelationId        /* OCLASS_DEFACL */
 };
 
@@ -2072,6 +2074,10 @@ getObjectClass(const ObjectAddress *object)
        case UserMappingRelationId:
            return OCLASS_USER_MAPPING;
 
+       case ForeignTableRelationId:
+           Assert(object->objectSubId == 0);
+           return OCLASS_FOREIGN_TABLE;
+
        case DefaultAclRelationId:
            return OCLASS_DEFACL;
    }
@@ -2754,6 +2760,10 @@ getRelationDescription(StringInfo buffer, Oid relid)
            appendStringInfo(buffer, _("composite type %s"),
                             relname);
            break;
+       case RELKIND_FOREIGN_TABLE:
+           appendStringInfo(buffer, _("foreign table %s"),
+                            relname);
+           break;
        default:
            /* shouldn't get here */
            appendStringInfo(buffer, _("relation %s"),
index bcf6caa2eef325c82c8d5f4160ecd37908fc3e3b..ccaf7ffc2983f1012d97dbf36afd6b8da44b74f6 100644 (file)
@@ -43,6 +43,7 @@
 #include "catalog/objectaccess.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_constraint.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_statistic.h"
@@ -122,9 +123,9 @@ static List *insert_ordered_unique_oid(List *list, Oid datum);
  */
 
 /*
- * The initializers below do not include the attoptions or attacl fields,
- * but that's OK - we're never going to reference anything beyond the
- * fixed-size portion of the structure anyway.
+ * The initializers below do not include the attoptions or
+ * attacl fields, but that's OK - we're never going to reference anything
+ * beyond the fixed-size portion of the structure anyway.
  */
 
 static FormData_pg_attribute a1 = {
@@ -269,6 +270,7 @@ heap_create(const char *relname,
    {
        case RELKIND_VIEW:
        case RELKIND_COMPOSITE_TYPE:
+       case RELKIND_FOREIGN_TABLE:
            create_storage = false;
 
            /*
@@ -772,7 +774,7 @@ AddNewRelationTuple(Relation pg_class_desc,
            new_rel_reltup->reltuples = 1;
            break;
        default:
-           /* Views, etc, have no disk storage */
+           /* Views and foreign tables, etc, have no disk storage */
            new_rel_reltup->relpages = 0;
            new_rel_reltup->reltuples = 0;
            break;
@@ -987,7 +989,8 @@ heap_create_with_catalog(const char *relname,
        /* Use binary-upgrade overrides if applicable */
        if (OidIsValid(binary_upgrade_next_heap_relfilenode) &&
            (relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE ||
-            relkind == RELKIND_VIEW || relkind == RELKIND_COMPOSITE_TYPE))
+            relkind == RELKIND_VIEW || relkind == RELKIND_COMPOSITE_TYPE ||
+            relkind == RELKIND_FOREIGN_TABLE))
        {
            relid = binary_upgrade_next_heap_relfilenode;
            binary_upgrade_next_heap_relfilenode = InvalidOid;
@@ -1012,6 +1015,7 @@ heap_create_with_catalog(const char *relname,
        {
            case RELKIND_RELATION:
            case RELKIND_VIEW:
+           case RELKIND_FOREIGN_TABLE:
                relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid,
                                              relnamespace);
                break;
@@ -1049,7 +1053,7 @@ heap_create_with_catalog(const char *relname,
     * Decide whether to create an array type over the relation's rowtype. We
     * do not create any array types for system catalogs (ie, those made
     * during initdb).  We create array types for regular relations, views,
-    * and composite types ... but not, eg, for toast tables or sequences.
+    * composite types and foreign tables ... but not, eg, for toast tables or sequences.
     */
    if (IsUnderPostmaster && (relkind == RELKIND_RELATION ||
                              relkind == RELKIND_VIEW ||
@@ -1570,11 +1574,33 @@ heap_drop_with_catalog(Oid relid)
                        "it is being used by active queries in this session",
                        RelationGetRelationName(rel))));
 
+   /*
+    * Delete pg_foreign_table tuple first.
+    */
+   if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+   {
+       Relation    rel;
+       HeapTuple   tuple;
+
+       rel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+       tuple = SearchSysCache(FOREIGNTABLEREL,
+                               ObjectIdGetDatum(relid), 0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+           elog(ERROR, "cache lookup failed for foreign table %u", relid);
+
+       simple_heap_delete(rel, &tuple->t_self);
+
+       ReleaseSysCache(tuple);
+       heap_close(rel, RowExclusiveLock);
+   }
+
    /*
     * Schedule unlinking of the relation's physical files at commit.
     */
    if (rel->rd_rel->relkind != RELKIND_VIEW &&
-       rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+       rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
+       rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
    {
        RelationDropStorage(rel);
    }
index 8d9790d196a8d8bd404f7300a742f4aef7e29a05..29be8e34d20d07de273bf755c90069ab66f10c51 100644 (file)
@@ -465,7 +465,7 @@ CREATE VIEW column_domain_usage AS
           AND a.attrelid = c.oid
           AND a.atttypid = t.oid
           AND t.typtype = 'd'
-          AND c.relkind IN ('r', 'v')
+          AND c.relkind IN ('r', 'v', 'f')
           AND a.attnum > 0
           AND NOT a.attisdropped
           AND pg_has_role(t.typowner, 'USAGE');
@@ -504,7 +504,7 @@ CREATE VIEW column_privileges AS
                   pr_c.relowner
            FROM (SELECT oid, relname, relnamespace, relowner, (aclexplode(relacl)).*
                  FROM pg_class
-                 WHERE relkind IN ('r', 'v')
+                 WHERE relkind IN ('r', 'v', 'f')
                 ) pr_c (oid, relname, relnamespace, relowner, grantor, grantee, prtype, grantable),
                 pg_attribute a
            WHERE a.attrelid = pr_c.oid
@@ -526,7 +526,7 @@ CREATE VIEW column_privileges AS
                 ) pr_a (attrelid, attname, grantor, grantee, prtype, grantable),
                 pg_class c
            WHERE pr_a.attrelid = c.oid
-                 AND relkind IN ('r','v')
+                 AND relkind IN ('r', 'v', 'f')
          ) x,
          pg_namespace nc,
          pg_authid u_grantor,
@@ -569,7 +569,7 @@ CREATE VIEW column_udt_usage AS
     WHERE a.attrelid = c.oid
           AND a.atttypid = t.oid
           AND nc.oid = c.relnamespace
-          AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v')
+          AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v', 'f')
           AND pg_has_role(coalesce(bt.typowner, t.typowner), 'USAGE');
 
 GRANT SELECT ON column_udt_usage TO PUBLIC;
@@ -692,7 +692,7 @@ CREATE VIEW columns AS
           AND nc.oid = c.relnamespace
           AND (NOT pg_is_other_temp_schema(nc.oid))
 
-          AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v')
+          AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v', 'f')
 
           AND (pg_has_role(c.relowner, 'USAGE')
                OR has_column_privilege(c.oid, a.attnum,
@@ -1793,6 +1793,7 @@ CREATE VIEW tables AS
              CASE WHEN nc.oid = pg_my_temp_schema() THEN 'LOCAL TEMPORARY'
                   WHEN c.relkind = 'r' THEN 'BASE TABLE'
                   WHEN c.relkind = 'v' THEN 'VIEW'
+                  WHEN c.relkind = 'f' THEN 'FOREIGN TABLE'
                   ELSE null END
              AS character_data) AS table_type,
 
@@ -1817,7 +1818,7 @@ CREATE VIEW tables AS
     FROM pg_namespace nc JOIN pg_class c ON (nc.oid = c.relnamespace)
            LEFT JOIN (pg_type t JOIN pg_namespace nt ON (t.typnamespace = nt.oid)) ON (c.reloftype = t.oid)
 
-    WHERE c.relkind IN ('r', 'v')
+    WHERE c.relkind IN ('r', 'v', 'f')
           AND (NOT pg_is_other_temp_schema(nc.oid))
           AND (pg_has_role(c.relowner, 'USAGE')
                OR has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
@@ -2129,7 +2130,7 @@ CREATE VIEW view_column_usage AS
           AND dt.refclassid = 'pg_catalog.pg_class'::regclass
           AND dt.refobjid = t.oid
           AND t.relnamespace = nt.oid
-          AND t.relkind IN ('r', 'v')
+          AND t.relkind IN ('r', 'v', 'f')
           AND t.oid = a.attrelid
           AND dt.refobjsubid = a.attnum
           AND pg_has_role(t.relowner, 'USAGE');
@@ -2199,7 +2200,7 @@ CREATE VIEW view_table_usage AS
           AND dt.refclassid = 'pg_catalog.pg_class'::regclass
           AND dt.refobjid = t.oid
           AND t.relnamespace = nt.oid
-          AND t.relkind IN ('r', 'v')
+          AND t.relkind IN ('r', 'v', 'f')
           AND pg_has_role(t.relowner, 'USAGE');
 
 GRANT SELECT ON view_table_usage TO PUBLIC;
@@ -2344,7 +2345,7 @@ CREATE VIEW element_types AS
                   'TABLE'::text, a.attnum, a.atttypid
            FROM pg_class c, pg_attribute a
            WHERE c.oid = a.attrelid
-                 AND c.relkind IN ('r', 'v')
+                 AND c.relkind IN ('r', 'v', 'f')
                  AND attnum > 0 AND NOT attisdropped
 
            UNION ALL
@@ -2481,6 +2482,60 @@ CREATE VIEW foreign_servers AS
 GRANT SELECT ON foreign_servers TO PUBLIC;
 
 
+/* Base view for foreign tables */
+CREATE VIEW _pg_foreign_tables AS
+    SELECT
+           CAST(current_database() AS sql_identifier) AS foreign_table_catalog,
+           n.nspname AS foreign_table_schema,
+           c.relname AS foreign_table_name,
+           t.ftoptions AS ftoptions,
+           CAST(current_database() AS sql_identifier) AS foreign_server_catalog,
+           CAST(srvname AS sql_identifier) AS foreign_server_name,
+           CAST(u.rolname AS sql_identifier) AS authorization_identifier
+    FROM pg_foreign_table t, pg_foreign_server s, pg_foreign_data_wrapper w,
+         pg_authid u, pg_namespace n, pg_class c
+    WHERE w.oid = s.srvfdw
+          AND u.oid = c.relowner
+          AND (pg_has_role(c.relowner, 'USAGE')
+               OR has_table_privilege(c.oid, 'SELECT')
+               OR has_any_column_privilege(c.oid, 'SELECT'))
+          AND n.oid = c.relnamespace
+          AND c.oid = t.ftrelid
+          AND c.relkind = 'f'
+          AND s.oid = t.ftserver;
+
+
+/*
+ * 24.8
+ * FOREIGN_TABLE_OPTIONS view
+ */
+CREATE VIEW foreign_table_options AS
+    SELECT foreign_table_catalog,
+           foreign_table_schema,
+           foreign_table_name,
+           CAST((pg_options_to_table(t.ftoptions)).option_name AS sql_identifier) AS option_name,
+           CAST((pg_options_to_table(t.ftoptions)).option_value AS character_data) AS option_value
+    FROM _pg_foreign_tables t;
+
+GRANT SELECT ON TABLE foreign_table_options TO PUBLIC;
+
+
+/*
+ * 24.9
+ * FOREIGN_TABLES view
+ */
+CREATE VIEW foreign_tables AS
+    SELECT foreign_table_catalog,
+           foreign_table_schema,
+           foreign_table_name,
+           foreign_server_catalog,
+           foreign_server_name
+    FROM _pg_foreign_tables;
+
+GRANT SELECT ON foreign_tables TO PUBLIC;
+
+
+
 /* Base view for user mappings */
 CREATE VIEW _pg_user_mappings AS
     SELECT um.oid,
index 6a37e61dba25103752086397cf54bf25651a4b24..9f5d423dfabf4a7715fa1877fa58070103bd0b8c 100644 (file)
@@ -111,6 +111,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
        case OBJECT_SEQUENCE:
        case OBJECT_TABLE:
        case OBJECT_VIEW:
+       case OBJECT_FOREIGN_TABLE:
            relation =
                get_relation_by_qualified_name(objtype, objname, lockmode);
            address.classId = RelationRelationId;
@@ -369,6 +370,13 @@ get_relation_by_qualified_name(ObjectType objtype, List *objname,
                         errmsg("\"%s\" is not a view",
                                RelationGetRelationName(relation))));
            break;
+       case OBJECT_FOREIGN_TABLE:
+           if (relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+               ereport(ERROR,
+                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                        errmsg("\"%s\" is not a foreign table",
+                               RelationGetRelationName(relation))));
+           break;
        default:
            elog(ERROR, "unrecognized objtype: %d", (int) objtype);
            break;
index 6c9ba1eb3595d95bbfb4fce67c17ee51416d20d7..05f6c800db1db3bd4e0c851805208da89f64c1ed 100644 (file)
@@ -95,6 +95,7 @@ ExecRenameStmt(RenameStmt *stmt)
        case OBJECT_COLUMN:
        case OBJECT_ATTRIBUTE:
        case OBJECT_TRIGGER:
+       case OBJECT_FOREIGN_TABLE:
            {
                Oid         relid;
 
@@ -108,6 +109,7 @@ ExecRenameStmt(RenameStmt *stmt)
                    case OBJECT_SEQUENCE:
                    case OBJECT_VIEW:
                    case OBJECT_INDEX:
+                   case OBJECT_FOREIGN_TABLE:
                        {
                            /*
                             * RENAME TABLE requires that we (still) hold
@@ -206,6 +208,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
        case OBJECT_SEQUENCE:
        case OBJECT_TABLE:
        case OBJECT_VIEW:
+       case OBJECT_FOREIGN_TABLE:
            CheckRelationOwnership(stmt->relation, true);
            AlterTableNamespace(stmt->relation, stmt->newschema,
                                stmt->objectType, AccessExclusiveLock);
index 15464c3dc66d35b254ba0f2f93228449265fcdfa..13c76b77371cbf48ffc48bfe2e9b5a3960f80108 100644 (file)
@@ -187,7 +187,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
        /* No need for a WARNING if we already complained during VACUUM */
        if (!(vacstmt->options & VACOPT_VACUUM))
            ereport(WARNING,
-                   (errmsg("skipping \"%s\" --- cannot analyze indexes, views, or special system tables",
+                   (errmsg("skipping \"%s\" --- cannot analyze indexes, views, foreign tables or special system tables",
                            RelationGetRelationName(onerel))));
        relation_close(onerel, ShareUpdateExclusiveLock);
        return;
@@ -1458,6 +1458,13 @@ acquire_inherited_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
            continue;
        }
 
+       /* Ignore child foreign tables */
+       if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+       {
+           heap_close(childrel, AccessShareLock);
+           continue;
+       }
+
        rels[nrels] = childrel;
        relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel);
        totalblocks += relblocks[nrels];
index b578818b4f342d5ad3bde83501a68efb058f9bdb..c0731ef2064cc7a6487813f154f11f2558c33f39 100644 (file)
@@ -91,6 +91,7 @@ CommentObject(CommentStmt *stmt)
        case OBJECT_SEQUENCE:
        case OBJECT_TABLE:
        case OBJECT_VIEW:
+       case OBJECT_FOREIGN_TABLE:
            if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
                               RelationGetRelationName(relation));
@@ -574,7 +575,7 @@ CheckAttributeComment(Relation relation)
                       RelationGetRelationName(relation));
 
    /*
-    * Allow comments only on columns of tables, views, and composite types
+    * Allow comments only on columns of tables, views, composite types, and foreign tables
     * (which are the only relkinds for which pg_dump will dump per-column
     * comments).  In particular we wish to disallow comments on index
     * columns, because the naming of an index's columns may change across PG
@@ -582,10 +583,11 @@ CheckAttributeComment(Relation relation)
     */
    if (relation->rd_rel->relkind != RELKIND_RELATION &&
        relation->rd_rel->relkind != RELKIND_VIEW &&
-       relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+       relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
+       relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                errmsg("\"%s\" is not a table, view, or composite type",
+                errmsg("\"%s\" is not a table, view, composite type, or foreign table",
                        RelationGetRelationName(relation))));
 }
 
index 71ba806851bbe2b565dd0a7446e80d40acfd4325..d3330a8053edd4c23762ed693225802407d05058 100644 (file)
 
 #include "access/heapam.h"
 #include "access/reloptions.h"
+#include "access/xact.h"
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_user_mapping.h"
 #include "commands/defrem.h"
 #include "foreign/foreign.h"
 #include "miscadmin.h"
+#include "nodes/execnodes.h"
 #include "parser/parse_func.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
-
+#include "utils/tqual.h"
 
 /*
  * Convert a DefElem list to the text array format that is used in
@@ -315,16 +319,69 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId)
  * Convert a validator function name passed from the parser to an Oid.
  */
 static Oid
-lookup_fdw_validator_func(List *validator)
+lookup_fdw_validator_func(DefElem *validator)
 {
    Oid         funcargtypes[2];
 
+   if (validator == NULL || validator->arg == NULL)
+       return InvalidOid;
+
    funcargtypes[0] = TEXTARRAYOID;
    funcargtypes[1] = OIDOID;
-   return LookupFuncName(validator, 2, funcargtypes, false);
+   return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
    /* return value is ignored, so we don't check the type */
 }
 
+static Oid
+lookup_fdw_handler_func(DefElem *handler)
+{
+   Oid handlerOid;
+
+   if (handler == NULL || handler->arg == NULL)
+       return InvalidOid;
+
+   /* check that handler have correct return type */
+   handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
+   if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
+   {
+       ereport(ERROR,
+           (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+            errmsg("function %s must return type \"fdw_handler\"",
+               NameListToString((List *) handler->arg))));
+   }
+
+   return handlerOid;
+}
+
+static void
+parse_func_options(List *func_options, DefElem **validator, DefElem **handler)
+{
+   ListCell       *cell;
+
+   *validator = NULL;
+   *handler = NULL;
+   foreach (cell, func_options)
+   {
+       DefElem    *def = lfirst(cell);
+
+       if (pg_strcasecmp(def->defname, "validator") == 0)
+       {
+           if (*validator)
+               elog(ERROR, "duplicated VALIDATOR");
+           *validator = def;
+       }
+       else if (pg_strcasecmp(def->defname, "handler") == 0)
+       {
+           if (*handler)
+               elog(ERROR, "duplicated HANDLER");
+           *handler = def;
+       }
+       else
+       {
+           elog(ERROR, "invalid option");
+       }
+   }
+}
 
 /*
  * Create a foreign-data wrapper
@@ -337,7 +394,10 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
    bool        nulls[Natts_pg_foreign_data_wrapper];
    HeapTuple   tuple;
    Oid         fdwId;
+   DefElem    *defvalidator;
+   DefElem    *defhandler;
    Oid         fdwvalidator;
+   Oid         fdwhandler;
    Datum       fdwoptions;
    Oid         ownerId;
 
@@ -373,12 +433,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
        DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
    values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
 
-   if (stmt->validator)
-       fdwvalidator = lookup_fdw_validator_func(stmt->validator);
-   else
-       fdwvalidator = InvalidOid;
+   /* determin which validator to be used (or not used at all) */
+   parse_func_options(stmt->func_options, &defvalidator, &defhandler);
+   fdwvalidator = lookup_fdw_validator_func(defvalidator);
+   fdwhandler = lookup_fdw_handler_func(defhandler);
 
    values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator;
+   values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = fdwhandler;
 
    nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
 
@@ -414,6 +475,21 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    }
 
+   if (fdwhandler != InvalidOid)
+   {
+       ObjectAddress myself;
+       ObjectAddress referenced;
+
+       myself.classId = ForeignDataWrapperRelationId;
+       myself.objectId = fdwId;
+       myself.objectSubId = 0;
+
+       referenced.classId = ProcedureRelationId;
+       referenced.objectId = fdwhandler;
+       referenced.objectSubId = 0;
+       recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+   }
+
    recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
 
    /* Post creation hook for new foreign data wrapper */
@@ -438,7 +514,10 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
    Oid         fdwId;
    bool        isnull;
    Datum       datum;
+   DefElem    *defvalidator;
+   DefElem    *defhandler;
    Oid         fdwvalidator;
+   Oid         fdwhandler;
 
    /* Must be super user */
    if (!superuser())
@@ -462,9 +541,11 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
    memset(repl_null, false, sizeof(repl_null));
    memset(repl_repl, false, sizeof(repl_repl));
 
-   if (stmt->change_validator)
+   parse_func_options(stmt->func_options, &defvalidator, &defhandler);
+
+   if (defvalidator)
    {
-       fdwvalidator = stmt->validator ? lookup_fdw_validator_func(stmt->validator) : InvalidOid;
+       fdwvalidator = lookup_fdw_validator_func(defvalidator);
        repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
        repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
 
@@ -472,7 +553,7 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
         * It could be that the options for the FDW, SERVER and USER MAPPING
         * are no longer valid with the new validator.  Warn about this.
         */
-       if (stmt->validator)
+       if (defvalidator->arg)
            ereport(WARNING,
             (errmsg("changing the foreign-data wrapper validator can cause "
                     "the options for dependent objects to become invalid")));
@@ -490,6 +571,34 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
        fdwvalidator = DatumGetObjectId(datum);
    }
 
+   if (defhandler)
+   {
+       fdwhandler = lookup_fdw_handler_func(defhandler);
+       repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
+       repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
+
+       /*
+        * It could be that the behavior of accessing foreign table changes
+        * with the new handler.  Warn about this.
+        */
+       if (defhandler->arg)
+           ereport(WARNING,
+            (errmsg("changing the foreign-data wrapper handler would change "
+                    "the behavior of accessing foreign tables")));
+   }
+   else
+   {
+       /*
+        * Validator is not changed, but we need it for validating options.
+        */
+       datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
+                               tp,
+                               Anum_pg_foreign_data_wrapper_fdwhandler,
+                               &isnull);
+       Assert(!isnull);
+       fdwhandler = DatumGetObjectId(datum);
+   }
+
    /*
     * Options specified, validate and update.
     */
@@ -1158,3 +1267,163 @@ RemoveUserMappingById(Oid umId)
 
    heap_close(rel, RowExclusiveLock);
 }
+
+/*
+ * Create a foreign table
+ * call after DefineRelation().
+ */
+void
+CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
+{
+   Relation    ftrel;
+   Datum       ftoptions;
+   Datum       values[Natts_pg_foreign_table];
+   bool        nulls[Natts_pg_foreign_table];
+   HeapTuple   tuple;
+   Oid         ftId;
+   AclResult   aclresult;
+   ObjectAddress myself;
+   ObjectAddress referenced;
+   Oid         ownerId;
+   ForeignDataWrapper *fdw;
+   ForeignServer *server;
+
+   /*
+    * Advance command counter to ensure the pg_attribute tuple visible; the
+    * tuple might be updated to add constraints in previous step.
+    */
+   CommandCounterIncrement();
+
+   /*
+    * For now the owner cannot be specified on create. Use effective user ID.
+    */
+   ownerId = GetUserId();
+
+   /*
+    * Check that the foreign server exists and that we have USAGE on it. Also
+    * get the actual FDW for option validation etc.
+    */
+   server = GetForeignServerByName(stmt->servername, false);
+   aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
+   if (aclresult != ACLCHECK_OK)
+       aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
+
+   fdw = GetForeignDataWrapper(server->fdwid);
+
+   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_table.
+    */
+   ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+   memset(values, 0, sizeof(values));
+   memset(nulls, false, sizeof(nulls));
+
+   values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
+   values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
+   /* Add table generic options */
+   ftoptions = transformGenericOptions(ForeignTableRelationId,
+                                       PointerGetDatum(NULL),
+                                       stmt->options,
+                                       fdw->fdwvalidator);
+
+   if (PointerIsValid(DatumGetPointer(ftoptions)))
+       values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
+   else
+       nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+   tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
+
+   /* pg_foreign_table don't have OID */
+   ftId = simple_heap_insert(ftrel, tuple);
+
+   CatalogUpdateIndexes(ftrel, tuple);
+
+   heap_freetuple(tuple);
+
+   /* Add pg_class dependency on the server */
+   myself.classId = RelationRelationId;
+   myself.objectId = relid;
+   myself.objectSubId = 0;
+
+   referenced.classId = ForeignServerRelationId;
+   referenced.objectId = server->serverid;
+   referenced.objectSubId = 0;
+   recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+   heap_close(ftrel, NoLock);
+}
+
+/*
+ * ALTER FOREIGN TABLE <name> OPTIONS (...)
+ */
+void
+ATExecGenericOptions(Relation rel, List *options)
+{
+   Relation            ftrel;
+   ForeignTable       *table;
+   ForeignServer      *server;
+   ForeignDataWrapper *fdw;
+   HeapTuple           tuple;
+   bool                isnull;
+   Datum               repl_val[Natts_pg_foreign_table];
+   bool                repl_null[Natts_pg_foreign_table];
+   bool                repl_repl[Natts_pg_foreign_table];
+   Datum               datum;
+
+   if (options == NIL) 
+       return;
+
+   tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("foreign table \"%s\" does not exist",
+                                       RelationGetRelationName(rel))));
+
+   table = GetForeignTable(rel->rd_id);
+   server = GetForeignServer(table->serverid);
+   fdw = GetForeignDataWrapper(server->fdwid);
+
+   memset(repl_val, 0, sizeof(repl_val));
+   memset(repl_null, false, sizeof(repl_null));
+   memset(repl_repl, false, sizeof(repl_repl));
+
+   /* Extract the current options */
+   datum = SysCacheGetAttr(FOREIGNTABLEREL,
+                           tuple,
+                           Anum_pg_foreign_table_ftoptions,
+                           &isnull);
+   if (isnull)
+       datum = PointerGetDatum(NULL);
+
+   /* Transform the options */
+   datum = transformGenericOptions(ForeignTableRelationId,
+                                   datum,
+                                   options,
+                                   fdw->fdwvalidator);
+
+   if (PointerIsValid(DatumGetPointer(datum)))
+       repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
+   else
+       repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+   repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+   /* Everything looks good - update the tuple */
+
+   ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+   tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
+                             repl_val, repl_null, repl_repl);
+
+   simple_heap_update(ftrel, &tuple->t_self, tuple);
+   CatalogUpdateIndexes(ftrel, tuple);
+
+   heap_close(ftrel, RowExclusiveLock);
+
+   heap_freetuple(tuple);
+}
index 35fc1b38804982ed210c00df6c66a718c6af23d1..5b4d5e26c0d40b06d28d51912285dba90ed9298a 100644 (file)
@@ -142,11 +142,12 @@ LockTableRecurse(Oid reloid, RangeVar *rv,
        aclcheck_error(aclresult, ACL_KIND_CLASS,
                       RelationGetRelationName(rel));
 
-   /* Currently, we only allow plain tables to be locked */
-   if (rel->rd_rel->relkind != RELKIND_RELATION)
+   /* Currently, we only allow plain tables and foreign tables to be locked */
+   if (rel->rd_rel->relkind != RELKIND_RELATION &&
+       rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                errmsg("\"%s\" is not a table",
+                errmsg("\"%s\" is not a table or foreign table",
                        RelationGetRelationName(rel))));
 
    /*
index 762bbae530ab343c2a7c5a3b9ba138a1ff5ad8ad..98f7e180f1c667826a86dacc2f53fd45209a9fc3 100644 (file)
@@ -103,6 +103,7 @@ ExecSecLabelStmt(SecLabelStmt *stmt)
        case OBJECT_SEQUENCE:
        case OBJECT_TABLE:
        case OBJECT_VIEW:
+       case OBJECT_FOREIGN_TABLE:
            if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
                               RelationGetRelationName(relation));
index 6729d8336f570e59fd514fef6bab75dd3bb16ff3..6a092515efc5202f05396be430462fdbf963aa22 100644 (file)
@@ -29,6 +29,7 @@
 #include "catalog/objectaccess.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_inherits_fn.h"
 #include "catalog/pg_namespace.h"
@@ -48,6 +49,7 @@
 #include "commands/trigger.h"
 #include "commands/typecmds.h"
 #include "executor/executor.h"
+#include "foreign/foreign.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -219,6 +221,12 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
        gettext_noop("type \"%s\" does not exist, skipping"),
        gettext_noop("\"%s\" is not a type"),
    gettext_noop("Use DROP TYPE to remove a type.")},
+   {RELKIND_FOREIGN_TABLE,
+       ERRCODE_UNDEFINED_OBJECT,
+       gettext_noop("foreign table \"%s\" does not exist"),
+       gettext_noop("foreign table \"%s\" does not exist, skipping"),
+       gettext_noop("\"%s\" is not a foreign table"),
+   gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
    {'\0', 0, NULL, NULL, NULL, NULL}
 };
 
@@ -264,7 +272,7 @@ static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 static void ATRewriteTables(List **wqueue, LOCKMODE lockmode);
 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
-static void ATSimplePermissions(Relation rel, bool allowView, bool allowType);
+static void ATSimplePermissions(Relation rel, bool allowView, bool allowType, bool allowForeignTable);
 static void ATSimplePermissionsRelationOrIndex(Relation rel);
 static void ATSimpleRecursion(List **wqueue, Relation rel,
                  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
@@ -675,7 +683,8 @@ DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
 
 /*
  * RemoveRelations
- *     Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW
+ *     Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
+ *      DROP FOREIGN TABLE
  */
 void
 RemoveRelations(DropStmt *drop)
@@ -709,6 +718,10 @@ RemoveRelations(DropStmt *drop)
            relkind = RELKIND_VIEW;
            break;
 
+       case OBJECT_FOREIGN_TABLE:
+           relkind = RELKIND_FOREIGN_TABLE;
+           break;
+
        default:
            elog(ERROR, "unrecognized drop object type: %d",
                 (int) drop->removeType);
@@ -1991,10 +2004,11 @@ renameatt_internal(Oid myrelid,
    if (relkind != RELKIND_RELATION &&
        relkind != RELKIND_VIEW &&
        relkind != RELKIND_COMPOSITE_TYPE &&
-       relkind != RELKIND_INDEX)
+       relkind != RELKIND_INDEX &&
+       relkind != RELKIND_FOREIGN_TABLE)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-              errmsg("\"%s\" is not a table, view, composite type or index",
+              errmsg("\"%s\" is not a table, view, composite type,index or foreign table",
                      RelationGetRelationName(targetrelation))));
 
    /*
@@ -2441,6 +2455,14 @@ AlterTable(AlterTableStmt *stmt)
                                RelationGetRelationName(rel))));
            break;
 
+       case OBJECT_FOREIGN_TABLE:
+           if (rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+               ereport(ERROR,
+                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                        errmsg("\"%s\" is not a foreign table",
+                               RelationGetRelationName(rel))));
+           break;
+
        default:
            elog(ERROR, "unrecognized object type: %d", (int) stmt->relkind);
    }
@@ -2526,6 +2548,7 @@ AlterTableGetLockLevel(List *cmds)
            case AT_SetTableSpace:      /* must rewrite heap */
            case AT_DropNotNull:        /* may change some SQL plans */
            case AT_SetNotNull:
+           case AT_GenericOptions:
                cmd_lockmode = AccessExclusiveLock;
                break;
 
@@ -2684,14 +2707,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
    switch (cmd->subtype)
    {
        case AT_AddColumn:      /* ADD COLUMN */
-           ATSimplePermissions(rel, false, true);
+           ATSimplePermissions(rel, false, true, true);
            /* Performs own recursion */
            ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
            pass = AT_PASS_ADD_COL;
            break;
        case AT_AddColumnToView:        /* add column via CREATE OR REPLACE
                                         * VIEW */
-           ATSimplePermissions(rel, true, false);
+           ATSimplePermissions(rel, true, false, false);
            /* Performs own recursion */
            ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
            pass = AT_PASS_ADD_COL;
@@ -2704,19 +2727,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
             * substitutes default values into INSERTs before it expands
             * rules.
             */
-           ATSimplePermissions(rel, true, false);
+           ATSimplePermissions(rel, true, false, true);
            ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
            /* No command-specific prep needed */
            pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
            break;
        case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, true);
            ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
            /* No command-specific prep needed */
            pass = AT_PASS_DROP;
            break;
        case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, true);
            ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
            /* No command-specific prep needed */
            pass = AT_PASS_ADD_CONSTR;
@@ -2734,25 +2757,25 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
            pass = AT_PASS_MISC;
            break;
        case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, false);
            ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
            /* No command-specific prep needed */
            pass = AT_PASS_MISC;
            break;
        case AT_DropColumn:     /* DROP COLUMN */
-           ATSimplePermissions(rel, false, true);
+           ATSimplePermissions(rel, false, true, true);
            ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
            /* Recursion occurs during execution phase */
            pass = AT_PASS_DROP;
            break;
        case AT_AddIndex:       /* ADD INDEX */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, false);
            /* This command never recurses */
            /* No command-specific prep needed */
            pass = AT_PASS_ADD_INDEX;
            break;
        case AT_AddConstraint:  /* ADD CONSTRAINT */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, true);
            /* Recursion occurs during execution phase */
            /* No command-specific prep needed except saving recurse flag */
            if (recurse)
@@ -2760,7 +2783,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
            pass = AT_PASS_ADD_CONSTR;
            break;
        case AT_DropConstraint: /* DROP CONSTRAINT */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, true);
            /* Recursion occurs during execution phase */
            /* No command-specific prep needed except saving recurse flag */
            if (recurse)
@@ -2768,7 +2791,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
            pass = AT_PASS_DROP;
            break;
        case AT_AlterColumnType:        /* ALTER COLUMN TYPE */
-           ATSimplePermissions(rel, false, true);
+           ATSimplePermissions(rel, false, true, true);
            /* Performs own recursion */
            ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
            pass = AT_PASS_ALTER_TYPE;
@@ -2780,20 +2803,20 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
            break;
        case AT_ClusterOn:      /* CLUSTER ON */
        case AT_DropCluster:    /* SET WITHOUT CLUSTER */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, false);
            /* These commands never recurse */
            /* No command-specific prep needed */
            pass = AT_PASS_MISC;
            break;
        case AT_AddOids:        /* SET WITH OIDS */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, false);
            /* Performs own recursion */
            if (!rel->rd_rel->relhasoids || recursing)
                ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
            pass = AT_PASS_ADD_COL;
            break;
        case AT_DropOids:       /* SET WITHOUT OIDS */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, false);
            /* Performs own recursion */
            if (rel->rd_rel->relhasoids)
            {
@@ -2820,7 +2843,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
            pass = AT_PASS_MISC;
            break;
        case AT_AddInherit:     /* INHERIT */
-           ATSimplePermissions(rel, false, false);
+           ATSimplePermissions(rel, false, false, true);
            /* This command never recurses */
            ATPrepAddInherit(rel);
            pass = AT_PASS_MISC;
@@ -2837,8 +2860,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
        case AT_EnableAlwaysRule:
        case AT_EnableReplicaRule:
        case AT_DisableRule:
+           ATSimplePermissions(rel, false, false, false);
+           /* These commands never recurse */
+           /* No command-specific prep needed */
+           pass = AT_PASS_MISC;
+           break;
        case AT_DropInherit:    /* NO INHERIT */
-           ATSimplePermissions(rel, false, false);
+       case AT_GenericOptions:
+           ATSimplePermissions(rel, false, false, true);
            /* These commands never recurse */
            /* No command-specific prep needed */
            pass = AT_PASS_MISC;
@@ -3085,6 +3114,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
        case AT_DropInherit:
            ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
            break;
+       case AT_GenericOptions:
+           ATExecGenericOptions(rel, (List *) cmd->def);
+           break;
        default:                /* oops */
            elog(ERROR, "unrecognized alter table type: %d",
                 (int) cmd->subtype);
@@ -3111,6 +3143,10 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
    {
        AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
 
+       /* If it's a foreign table, no need to rewrite it. */
+       if (tab->relkind == RELKIND_FOREIGN_TABLE)
+           continue;
+
        /*
         * We only need to rewrite the table if at least one column needs to
         * be recomputed, or we are adding/removing the OID column.
@@ -3564,36 +3600,37 @@ ATGetQueueEntry(List **wqueue, Relation rel)
  * - Ensure that it is not a system table
  */
 static void
-ATSimplePermissions(Relation rel, bool allowView, bool allowType)
+ATSimplePermissions(Relation rel, bool allowView, bool allowType, bool allowForeignTable)
 {
+   int     relkind = rel->rd_rel->relkind;
+
    if (rel->rd_rel->relkind != RELKIND_RELATION)
    {
-       if (allowView)
-       {
-           if (rel->rd_rel->relkind != RELKIND_VIEW)
-               ereport(ERROR,
-                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                        errmsg("\"%s\" is not a table or view",
-                               RelationGetRelationName(rel))));
-       }
-       else if (allowType)
+       StringInfoData  allowed;
+
+       initStringInfo(&allowed);
+       appendStringInfo(&allowed, "table%s%s%s",
+           allowView ? " or view" : "",
+           allowType ? " or composite type" : "",
+           allowForeignTable ? " or foreign table" : "");
+       
+       if ((relkind == RELKIND_VIEW && !allowView) ||
+           (relkind == RELKIND_COMPOSITE_TYPE && !allowType) ||
+           (relkind == RELKIND_FOREIGN_TABLE && !allowForeignTable))
        {
-           if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
-               ereport(ERROR,
-                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                        errmsg("\"%s\" is not a table or composite type",
-                               RelationGetRelationName(rel))));
-       }
-       else
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                    errmsg("\"%s\" is not a table",
-                           RelationGetRelationName(rel))));
+                    errmsg("\"%s\" is not a %s",
+                           RelationGetRelationName(rel), allowed.data)));
+       }
+       pfree(allowed.data);
    }
 
    /* Permissions checks */
    if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
-       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+       aclcheck_error(ACLCHECK_NOT_OWNER,
+                      relkind == RELKIND_FOREIGN_TABLE ?
+                      ACL_KIND_FOREIGN_TABLE : ACL_KIND_CLASS,
                       RelationGetRelationName(rel));
 
    if (!allowSystemTableMods && IsSystemRelation(rel))
@@ -4692,7 +4729,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
 
    /* At top level, permission check was done in ATPrepCmd, else do it */
    if (recursing)
-       ATSimplePermissions(rel, false, true);
+       ATSimplePermissions(rel, false, true, true);
 
    /*
     * get the number of the attribute
@@ -4996,7 +5033,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
    /* At top level, permission check was done in ATPrepCmd, else do it */
    if (recursing)
-       ATSimplePermissions(rel, false, false);
+       ATSimplePermissions(rel, false, false, true);
 
    /*
     * Call AddRelationNewConstraints to do the work, making sure it works on
@@ -5940,7 +5977,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
 
    /* At top level, permission check was done in ATPrepCmd, else do it */
    if (recursing)
-       ATSimplePermissions(rel, false, false);
+       ATSimplePermissions(rel, false, false, true);
 
    conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
 
@@ -6827,6 +6864,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
    {
        case RELKIND_RELATION:
        case RELKIND_VIEW:
+       case RELKIND_FOREIGN_TABLE:
            /* ok to change owner */
            break;
        case RELKIND_INDEX:
@@ -6883,7 +6921,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
        default:
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                    errmsg("\"%s\" is not a table, view, or sequence",
+                    errmsg("\"%s\" is not a table, view, sequence, or foreign tabl, or foreign tablee",
                            NameStr(tuple_class->relname))));
    }
 
@@ -7543,7 +7581,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
     * Must be owner of both parent and child -- child was checked by
     * ATSimplePermissions call in ATPrepCmd
     */
-   ATSimplePermissions(parent_rel, false, false);
+   ATSimplePermissions(parent_rel, false, false, true);
 
    /* Permanent rels cannot inherit from temporary ones */
    if (RelationUsesTempNamespace(parent_rel)
@@ -8149,6 +8187,14 @@ AlterTableNamespace(RangeVar *relation, const char *newschema,
                                RelationGetRelationName(rel))));
            break;
 
+       case OBJECT_FOREIGN_TABLE:
+           if (rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+               ereport(ERROR,
+                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                        errmsg("\"%s\" is not a foreign table",
+                               RelationGetRelationName(rel))));
+           break;
+
        default:
            elog(ERROR, "unrecognized object type: %d", (int) stmttype);
    }
@@ -8158,6 +8204,7 @@ AlterTableNamespace(RangeVar *relation, const char *newschema,
    {
        case RELKIND_RELATION:
        case RELKIND_VIEW:
+       case RELKIND_FOREIGN_TABLE:
            /* ok to change schema */
            break;
        case RELKIND_SEQUENCE:
@@ -8188,7 +8235,7 @@ AlterTableNamespace(RangeVar *relation, const char *newschema,
        default:
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                    errmsg("\"%s\" is not a table, view, or sequence",
+                    errmsg("\"%s\" is not a table, view, sequence, or foreign table",
                            RelationGetRelationName(rel))));
    }
 
index 2f68df41c3368ed3a9c939f4a566c49fdf03770e..143ec79c4392d0597f2837e76713c39b091c5e5a 100644 (file)
@@ -894,7 +894,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
        onerel->rd_rel->relkind != RELKIND_TOASTVALUE)
    {
        ereport(WARNING,
-               (errmsg("skipping \"%s\" --- cannot vacuum indexes, views, or special system tables",
+               (errmsg("skipping \"%s\" --- cannot vacuum indexes, views, foreign tables or special system tables",
                        RelationGetRelationName(onerel))));
        relation_close(onerel, lmode);
        PopActiveSnapshot();
index cc67bcb5a5cfe450e12d744689193cc449c07c34..7e7ca86709f34f4de4b78f717ac10c51ee62ff5a 100644 (file)
@@ -16,6 +16,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_user_mapping.h"
 #include "foreign/foreign.h"
@@ -59,6 +60,7 @@ GetForeignDataWrapper(Oid fdwid)
    fdw->owner = fdwform->fdwowner;
    fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
    fdw->fdwvalidator = fdwform->fdwvalidator;
+   fdw->fdwhandler = fdwform->fdwhandler;
 
    /* Extract the options */
    datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
@@ -414,3 +416,138 @@ postgresql_fdw_validator(PG_FUNCTION_ARGS)
 
    PG_RETURN_BOOL(true);
 }
+
+/*
+ * GetForeignTable - look up the foreign table definition by relation oid.
+ */
+ForeignTable *
+GetForeignTable(Oid relid)
+{
+   Form_pg_foreign_table tableform;
+   ForeignTable *ft;
+   HeapTuple   tp;
+   Datum       datum;
+   bool        isnull;
+
+   tp = SearchSysCache(FOREIGNTABLEREL,
+                       ObjectIdGetDatum(relid),
+                       0, 0, 0);
+
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for foreign table %u", relid);
+
+   tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
+
+   ft = palloc(sizeof(ForeignTable));
+   ft->relid = relid;
+   ft->serverid = tableform->ftserver;
+
+   /* Extract the ftoptions */
+   datum = SysCacheGetAttr(FOREIGNTABLEREL,
+                           tp,
+                           Anum_pg_foreign_table_ftoptions,
+                           &isnull);
+
+   /* untransformRelOptions does exactly what we want - avoid duplication */
+   ft->options = untransformRelOptions(datum);
+   ReleaseSysCache(tp);
+
+   return ft;
+}
+
+/*
+ * Determine the relation is a foreign table.
+ */
+bool
+IsForeignTable(Oid relid)
+{
+   HeapTuple   tuple;
+   Form_pg_class classForm;
+   char        relkind;
+
+   tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+              (errcode(ERRCODE_UNDEFINED_TABLE),
+               errmsg("relation with OID %u does not exist", relid)));
+   classForm = (Form_pg_class) GETSTRUCT(tuple);
+   relkind = classForm->relkind;
+   ReleaseSysCache(tuple);
+
+   return (relkind == RELKIND_FOREIGN_TABLE);
+}
+
+/*
+ * Get fdwvalidator of the foreign table without generating ForeignTable,
+ * ForeignServer and ForeignDataWrapper.
+ */
+Oid
+GetFdwValidator(Oid relid)
+{
+   HeapTuple   tuple;
+   Oid         serverid;
+   Oid         fdwid;
+   Oid         fdwvalidator;
+   Form_pg_foreign_table           form_ft;
+   Form_pg_foreign_server          form_srv;
+   Form_pg_foreign_data_wrapper    form_fdw;
+
+   /* pg_foreign_table */
+   tuple = SearchSysCache(FOREIGNTABLEREL,
+                          ObjectIdGetDatum(relid),
+                          0, 0, 0);
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("foreign table reference \"%u\" is ambiguous", relid)));
+   form_ft = (Form_pg_foreign_table) GETSTRUCT(tuple);
+   serverid = form_ft->ftserver;
+   ReleaseSysCache(tuple);
+
+   /* pg_foreign_server */
+   tuple = SearchSysCache1(FOREIGNSERVEROID,
+                           ObjectIdGetDatum(serverid));
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("foreign server reference \"%u\" is ambiguous",
+                       serverid)));
+   form_srv = (Form_pg_foreign_server) GETSTRUCT(tuple);
+   fdwid = form_srv->srvfdw;
+   ReleaseSysCache(tuple);
+
+   /* pg_foreign_table */
+   tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID,
+                           ObjectIdGetDatum(fdwid));
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("foreign-data wrapper reference \"%u\" is ambiguous",
+                       fdwid)));
+   form_fdw = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
+   fdwvalidator = form_fdw->fdwvalidator;
+   ReleaseSysCache(tuple);
+
+   return fdwvalidator;
+}
+
+/*
+ * Flattern generic options into keywords and values buffers.
+ */
+int
+flatten_generic_options(List *options, const char **keywords,
+                       const char **values)
+{
+   ListCell   *cell;
+   int         n = 0;
+
+   foreach(cell, options)
+   {
+       DefElem    *def = lfirst(cell);
+
+       keywords[n] = def->defname;
+       values[n] = strVal(def->arg);
+       n++;
+   }
+   return n;
+}
index 4e1f221af1b2ed70a7c3165c18049a29d03717d9..60970443d7e087d95a3bd597bc89080f5279beb1 100644 (file)
@@ -3191,7 +3191,7 @@ _copyCreateFdwStmt(CreateFdwStmt *from)
    CreateFdwStmt *newnode = makeNode(CreateFdwStmt);
 
    COPY_STRING_FIELD(fdwname);
-   COPY_NODE_FIELD(validator);
+   COPY_NODE_FIELD(func_options);
    COPY_NODE_FIELD(options);
 
    return newnode;
@@ -3203,8 +3203,7 @@ _copyAlterFdwStmt(AlterFdwStmt *from)
    AlterFdwStmt *newnode = makeNode(AlterFdwStmt);
 
    COPY_STRING_FIELD(fdwname);
-   COPY_NODE_FIELD(validator);
-   COPY_SCALAR_FIELD(change_validator);
+   COPY_NODE_FIELD(func_options);
    COPY_NODE_FIELD(options);
 
    return newnode;
@@ -3297,6 +3296,25 @@ _copyDropUserMappingStmt(DropUserMappingStmt *from)
    return newnode;
 }
 
+static CreateForeignTableStmt *
+_copyCreateForeignTableStmt(CreateForeignTableStmt *from)
+{
+   CreateForeignTableStmt *newnode = makeNode(CreateForeignTableStmt);
+
+   COPY_NODE_FIELD(base.relation);
+   COPY_NODE_FIELD(base.tableElts);
+   COPY_NODE_FIELD(base.inhRelations);
+   COPY_NODE_FIELD(base.ofTypename);
+   COPY_NODE_FIELD(base.constraints);
+   COPY_NODE_FIELD(base.options);
+   COPY_SCALAR_FIELD(base.oncommit);
+   COPY_STRING_FIELD(base.tablespacename);
+   COPY_STRING_FIELD(servername);
+   COPY_NODE_FIELD(options);
+
+   return newnode;
+}
+
 static CreateTrigStmt *
 _copyCreateTrigStmt(CreateTrigStmt *from)
 {
@@ -4198,6 +4216,9 @@ copyObject(void *from)
        case T_DropUserMappingStmt:
            retval = _copyDropUserMappingStmt(from);
            break;
+       case T_CreateForeignTableStmt:
+           retval = _copyCreateForeignTableStmt(from);
+           break;
        case T_CreateTrigStmt:
            retval = _copyCreateTrigStmt(from);
            break;
index 85cded0f74dd329297491be46282d5b8ec80ae0e..51120c8c9173d67cbcc92ee5e494f0afd084adfe 100644 (file)
@@ -1628,7 +1628,7 @@ static bool
 _equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b)
 {
    COMPARE_STRING_FIELD(fdwname);
-   COMPARE_NODE_FIELD(validator);
+   COMPARE_NODE_FIELD(func_options);
    COMPARE_NODE_FIELD(options);
 
    return true;
@@ -1638,8 +1638,7 @@ static bool
 _equalAlterFdwStmt(AlterFdwStmt *a, AlterFdwStmt *b)
 {
    COMPARE_STRING_FIELD(fdwname);
-   COMPARE_NODE_FIELD(validator);
-   COMPARE_SCALAR_FIELD(change_validator);
+   COMPARE_NODE_FIELD(func_options);
    COMPARE_NODE_FIELD(options);
 
    return true;
@@ -1718,6 +1717,19 @@ _equalDropUserMappingStmt(DropUserMappingStmt *a, DropUserMappingStmt *b)
    return true;
 }
 
+static bool
+_equalCreateForeignTableStmt(CreateForeignTableStmt *a, CreateForeignTableStmt *b)
+{
+   if (!_equalCreateStmt(&a->base, &b->base))
+       return false;
+
+   COMPARE_STRING_FIELD(base.tablespacename);
+   COMPARE_STRING_FIELD(servername);
+   COMPARE_NODE_FIELD(options);
+
+   return true;
+}
+
 static bool
 _equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
 {
@@ -2821,6 +2833,9 @@ equal(void *a, void *b)
        case T_DropUserMappingStmt:
            retval = _equalDropUserMappingStmt(a, b);
            break;
+       case T_CreateForeignTableStmt:
+           retval = _equalCreateForeignTableStmt(a, b);
+           break;
        case T_CreateTrigStmt:
            retval = _equalCreateTrigStmt(a, b);
            break;
index 3d84f61e0aa0c00d0eee7610c39e24763709c537..6f05d5900a4a2db5f2db03a4c883f645e7a386d7 100644 (file)
@@ -28,6 +28,7 @@
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/pg_list.h"
 #include "optimizer/var.h"
 #include "parser/analyze.h"
 #include "parser/parse_agg.h"
@@ -40,6 +41,7 @@
 #include "parser/parse_target.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
+#include "utils/lsyscache.h"
 #include "utils/rel.h"
 
 
@@ -2281,6 +2283,15 @@ applyLockingClause(Query *qry, Index rtindex,
                   bool forUpdate, bool noWait, bool pushedDown)
 {
    RowMarkClause *rc;
+   RangeTblEntry *rte;
+
+   /* If rangetable is a foreign table, locking is not allowed */
+   rte = list_nth(qry->rtable, rtindex - 1);
+   if (rte->rtekind == RTE_RELATION &&
+       get_rel_relkind(rte->relid) == RELKIND_FOREIGN_TABLE)
+       ereport(ERROR,
+           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+            errmsg("SELECT FOR UPDATE/SHARE is not allowed with foreign tables")));
 
    /* If it's an explicit clause, make sure hasForUpdate gets set */
    if (!pushedDown)
index 8fc79b63377c93159f4a069e61dd457d39d1dbc8..194ca886d29134897bc2c2378af360b7ad7d389b 100644 (file)
@@ -185,6 +185,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
        AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
        AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
        AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
+       AlterForeignTableStmt
        AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
        AlterRoleStmt AlterRoleSetStmt
        AlterDefaultPrivilegesStmt DefACLAction
@@ -193,7 +194,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
        CreateDomainStmt CreateGroupStmt CreateOpClassStmt
        CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
        CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
-       CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
+       CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+       CreateAssertStmt CreateTrigStmt
        CreateUserStmt CreateUserMappingStmt CreateRoleStmt
        CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
        DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
@@ -219,8 +221,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <node>   alter_column_default opclass_item opclass_drop alter_using
 %type <ival>   add_drop opt_asc_desc opt_nulls_order
 
-%type <node>   alter_table_cmd alter_type_cmd
-%type <list>   alter_table_cmds alter_type_cmds
+%type <node>   alter_table_cmd alter_type_cmd alter_foreign_table_cmd
+%type <list>   alter_table_cmds alter_type_cmds alter_foreign_table_cmds
 
 %type <dbehavior>  opt_drop_behavior
 
@@ -279,6 +281,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <list>   stmtblock stmtmulti
                OptTableElementList TableElementList OptInherit definition
                OptTypedTableElementList TypedTableElementList
+               OptForeignTableElementList ForeignTableElementList
                reloptions opt_reloptions
                OptWith opt_distinct opt_definition func_args func_args_list
                func_args_with_defaults func_args_with_defaults_list
@@ -303,6 +306,9 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
                create_generic_options alter_generic_options
                relation_expr_list dostmt_opt_list
 
+%type <list>   opt_func_options func_options
+%type <defelt> fdw_option
+
 %type <range>  OptTempTableName
 %type <into>   into_clause create_as_target
 
@@ -351,7 +357,11 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <vsetstmt> set_rest SetResetClause
 
 %type <node>   TableElement TypedTableElement ConstraintElem TableFuncElement
+               ForeignTableElement ForeignConstraintElem
+%type <node>   CheckConstraintElem UniqueConstraintElem PrimaryKeyConstraintElem
+               ExcludeConstraintElem ForeignKeyConstraintElem
 %type <node>   columnDef columnOptions
+               foreignColumnDef
 %type <defelt> def_elem reloption_elem old_aggr_elem
 %type <node>   def_arg columnElem where_clause where_or_current_clause
                a_expr b_expr c_expr func_expr AexprConst indirection_el
@@ -411,10 +421,15 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <keyword> unreserved_keyword type_func_name_keyword
 %type <keyword> col_name_keyword reserved_keyword
 
-%type <node>   TableConstraint TableLikeClause
+%type <node>   TableConstraint TableLikeClause ForeignTableLikeClause
+               ForeignTableConstraint
 %type <ival>   TableLikeOptionList TableLikeOption
-%type <list>   ColQualList
+%type <ival>   ForeignTableLikeOptionList ForeignTableLikeOption
+%type <list>   ColQualList ForeignColQualList
 %type <node>   ColConstraint ColConstraintElem ConstraintAttr
+               ColNotNullConstraintElem ColNullConstraintElem
+               ColCheckConstraintElem
+               ForeignColConstraint ForeignColConstraintElem
 %type <ival>   key_actions key_delete key_match key_update key_action
 %type <ival>   ConstraintAttributeSpec ConstraintDeferrabilitySpec
                ConstraintTimeSpec
@@ -656,6 +671,7 @@ stmt :
            | AlterEnumStmt
            | AlterFdwStmt
            | AlterForeignServerStmt
+           | AlterForeignTableStmt
            | AlterFunctionStmt
            | AlterGroupStmt
            | AlterObjectSchemaStmt
@@ -684,6 +700,7 @@ stmt :
            | CreateDomainStmt
            | CreateFdwStmt
            | CreateForeignServerStmt
+           | CreateForeignTableStmt
            | CreateFunctionStmt
            | CreateGroupStmt
            | CreateOpClassStmt
@@ -2456,20 +2473,8 @@ ColConstraint:
  * or be part of a_expr NOT LIKE or similar constructs).
  */
 ColConstraintElem:
-           NOT NULL_P
-               {
-                   Constraint *n = makeNode(Constraint);
-                   n->contype = CONSTR_NOTNULL;
-                   n->location = @1;
-                   $$ = (Node *)n;
-               }
-           | NULL_P
-               {
-                   Constraint *n = makeNode(Constraint);
-                   n->contype = CONSTR_NULL;
-                   n->location = @1;
-                   $$ = (Node *)n;
-               }
+           ColNotNullConstraintElem                { $$ = $1; }
+           | ColNullConstraintElem                 { $$ = $1; }
            | UNIQUE opt_definition OptConsTableSpace
                {
                    Constraint *n = makeNode(Constraint);
@@ -2490,15 +2495,7 @@ ColConstraintElem:
                    n->indexspace = $4;
                    $$ = (Node *)n;
                }
-           | CHECK '(' a_expr ')'
-               {
-                   Constraint *n = makeNode(Constraint);
-                   n->contype = CONSTR_CHECK;
-                   n->location = @1;
-                   n->raw_expr = $3;
-                   n->cooked_expr = NULL;
-                   $$ = (Node *)n;
-               }
+           | ColCheckConstraintElem                { $$ = $1; }
            | DEFAULT b_expr
                {
                    Constraint *n = makeNode(Constraint);
@@ -2524,6 +2521,35 @@ ColConstraintElem:
                }
        ;
 
+ColNotNullConstraintElem: NOT NULL_P
+               {
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_NOTNULL;
+                   n->location = @1;
+                   $$ = (Node *)n;
+               }
+       ;
+
+ColNullConstraintElem: NULL_P
+               {
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_NULL;
+                   n->location = @1;
+                   $$ = (Node *)n;
+               }
+       ;
+
+ColCheckConstraintElem: CHECK '(' a_expr ')'
+               {
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_CHECK;
+                   n->location = @1;
+                   n->raw_expr = $3;
+                   n->cooked_expr = NULL;
+                   $$ = (Node *)n;
+               }
+       ;
+
 /*
  * ConstraintAttr represents constraint attributes, which we parse as if
  * they were independent constraint clauses, in order to avoid shift/reduce
@@ -2601,6 +2627,30 @@ TableLikeOption:
                | ALL               { $$ = CREATE_TABLE_LIKE_ALL; }
        ;
 
+ForeignTableLikeClause:
+           LIKE qualified_name ForeignTableLikeOptionList
+               {
+                   InhRelation *n = makeNode(InhRelation);
+                   n->relation = $2;
+                   n->options = $3;
+                   $$ = (Node *)n;
+               }
+       ;
+
+ForeignTableLikeOptionList:
+           ForeignTableLikeOptionList INCLUDING ForeignTableLikeOption
+                                               { $$ = $1 | $3; }
+           | ForeignTableLikeOptionList EXCLUDING ForeignTableLikeOption
+                                               { $$ = $1 & ~$3; }
+           | /* EMPTY */                       { $$ = 0; }
+       ;
+
+ForeignTableLikeOption:
+           CONSTRAINTS         { $$ = CREATE_TABLE_LIKE_CONSTRAINTS; }
+           | COMMENTS          { $$ = CREATE_TABLE_LIKE_COMMENTS; }
+           | ALL               { $$ = CREATE_TABLE_LIKE_FOREIGN_ALL; }
+       ;
+
 
 /* ConstraintElem specifies constraint syntax which is not embedded into
  * a column definition. ColConstraintElem specifies the embedded form.
@@ -2618,7 +2668,32 @@ TableConstraint:
            | ConstraintElem                        { $$ = $1; }
        ;
 
+ForeignTableConstraint:
+           CONSTRAINT name ForeignConstraintElem
+               {
+                   Constraint *n = (Constraint *) $3;
+                   Assert(IsA(n, Constraint));
+                   n->conname = $2;
+                   n->location = @1;
+                   $$ = (Node *) n;
+               }
+           | ForeignConstraintElem                 { $$ = $1; }
+       ;
+
+
 ConstraintElem:
+           CheckConstraintElem                     { $$ = $1; }
+           | UniqueConstraintElem                  { $$ = $1; }
+           | PrimaryKeyConstraintElem              { $$ = $1; }
+           | ExcludeConstraintElem                 { $$ = $1; }
+           | ForeignKeyConstraintElem              { $$ = $1; }
+   ;
+
+ForeignConstraintElem:
+           CheckConstraintElem                     { $$ = $1; }
+   ;
+
+CheckConstraintElem:
            CHECK '(' a_expr ')' ConstraintAttributeSpec
                {
                    Constraint *n = makeNode(Constraint);
@@ -2633,7 +2708,10 @@ ConstraintElem:
                                 parser_errposition(@5)));
                    $$ = (Node *)n;
                }
-           | UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
+       ;
+
+UniqueConstraintElem:
+           UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
                ConstraintAttributeSpec
                {
                    Constraint *n = makeNode(Constraint);
@@ -2646,7 +2724,10 @@ ConstraintElem:
                    n->initdeferred = ($7 & 2) != 0;
                    $$ = (Node *)n;
                }
-           | PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
+       ;
+
+PrimaryKeyConstraintElem:
+           PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
                ConstraintAttributeSpec
                {
                    Constraint *n = makeNode(Constraint);
@@ -2659,7 +2740,10 @@ ConstraintElem:
                    n->initdeferred = ($8 & 2) != 0;
                    $$ = (Node *)n;
                }
-           | EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
+       ;
+
+ExcludeConstraintElem:
+           EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
                opt_definition OptConsTableSpace ExclusionWhereClause
                ConstraintAttributeSpec
                {
@@ -2675,7 +2759,9 @@ ConstraintElem:
                    n->initdeferred     = ($9 & 2) != 0;
                    $$ = (Node *)n;
                }
-           | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
+       ;
+ForeignKeyConstraintElem:
+           FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
                opt_column_list key_match key_actions ConstraintAttributeSpec
                {
                    Constraint *n = makeNode(Constraint);
@@ -3137,16 +3223,33 @@ DropTableSpaceStmt: DROP TABLESPACE name
  *
  *****************************************************************************/
 
-CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER name opt_validator create_generic_options
+CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER name opt_func_options create_generic_options
                {
                    CreateFdwStmt *n = makeNode(CreateFdwStmt);
                    n->fdwname = $5;
-                   n->validator = $6;
+                   n->func_options = $6;
                    n->options = $7;
                    $$ = (Node *) n;
                }
        ;
 
+fdw_option:
+           VALIDATOR handler_name              { $$ = makeDefElem("validator", (Node *)$2); }
+           | NO VALIDATOR                      { $$ = makeDefElem("validator", NULL); }
+           | HANDLER handler_name              { $$ = makeDefElem("handler", (Node *)$2); }
+           | NO HANDLER                        { $$ = makeDefElem("handler", NULL); }
+       ;
+
+func_options:
+           fdw_option                          { $$ = list_make1($1); }
+           | func_options fdw_option           { $$ = lappend($1, $2); }
+       ;
+
+opt_func_options:
+           func_options                        { $$ = $1; }
+           | /*EMPTY*/                         { $$ = NIL; }
+       ;
+
 /*****************************************************************************
  *
  *         QUERY :
@@ -3179,28 +3282,20 @@ DropFdwStmt: DROP FOREIGN DATA_P WRAPPER name opt_drop_behavior
  *
  ****************************************************************************/
 
-AlterFdwStmt: ALTER FOREIGN DATA_P WRAPPER name validator_clause alter_generic_options
+AlterFdwStmt: ALTER FOREIGN DATA_P WRAPPER name opt_func_options alter_generic_options
                {
                    AlterFdwStmt *n = makeNode(AlterFdwStmt);
                    n->fdwname = $5;
-                   n->validator = $6;
-                   n->change_validator = true;
+                   n->func_options = $6;
                    n->options = $7;
                    $$ = (Node *) n;
                }
-           | ALTER FOREIGN DATA_P WRAPPER name validator_clause
+           | ALTER FOREIGN DATA_P WRAPPER name func_options
                {
                    AlterFdwStmt *n = makeNode(AlterFdwStmt);
                    n->fdwname = $5;
-                   n->validator = $6;
-                   n->change_validator = true;
-                   $$ = (Node *) n;
-               }
-           | ALTER FOREIGN DATA_P WRAPPER name alter_generic_options
-               {
-                   AlterFdwStmt *n = makeNode(AlterFdwStmt);
-                   n->fdwname = $5;
-                   n->options = $6;
+                   n->func_options = $6;
+                   n->options = NIL;
                    $$ = (Node *) n;
                }
        ;
@@ -3369,6 +3464,248 @@ AlterForeignServerStmt: ALTER SERVER name foreign_server_version alter_generic_o
                }
        ;
 
+/*****************************************************************************
+ *
+ *         QUERY:
+ *             CREATE FORIGN TABLE relname (...) SERVER name (...)
+ *
+ *****************************************************************************/
+
+CreateForeignTableStmt:
+       CREATE FOREIGN TABLE qualified_name
+           OptForeignTableElementList OptInherit
+           SERVER name create_generic_options
+               {
+                   CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt);
+                   $4->relpersistence = RELPERSISTENCE_PERMANENT;
+                   n->base.relation = $4;
+                   n->base.tableElts = $5;
+                   n->base.inhRelations = $6;
+                   /* FDW-specific data */
+                   n->servername = $8;
+                   n->options = $9;
+                   $$ = (Node *) n;
+               }
+       ;
+
+OptForeignTableElementList:
+           '(' ForeignTableElementList ')'         { $$ = $2; }
+           | '(' ')'                               { $$ = NIL; }
+           | /*EMPTY*/
+               {
+                   /* TODO: import definitions from foreign server in this case. */
+                   $$ = NIL;
+               }
+       ;
+
+ForeignTableElementList:
+           ForeignTableElement
+               {
+                   $$ = list_make1($1);
+               }
+           | ForeignTableElementList ',' ForeignTableElement
+               {
+                   $$ = lappend($1, $3);
+               }
+       ;
+
+ForeignTableElement:
+           foreignColumnDef                    { $$ = $1; }
+           | ForeignTableLikeClause            { $$ = $1; }
+           | ForeignTableConstraint            { $$ = $1; }
+       ;
+
+foreignColumnDef:  ColId Typename ForeignColQualList
+               {
+                   ColumnDef *n = makeNode(ColumnDef);
+                   n->colname = $1;
+                   n->typeName = $2;
+                   n->constraints = $3;
+                   n->is_local = true;
+                   $$ = (Node *)n;
+               }
+       ;
+
+ForeignColQualList:
+           ForeignColQualList ForeignColConstraint { $$ = lappend($1, $2); }
+           | /*EMPTY*/                             { $$ = NIL; }
+       ;
+
+ForeignColConstraint:
+           CONSTRAINT name ForeignColConstraintElem
+               {
+                   Constraint *n = (Constraint *) $3;
+                   Assert(IsA(n, Constraint));
+                   n->conname = $2;
+                   n->location = @1;
+                   $$ = (Node *) n;
+               }
+           | ForeignColConstraintElem              { $$ = $1; }
+       ;
+
+ForeignColConstraintElem:
+           ColNotNullConstraintElem                { $$ = $1; }
+           | ColNullConstraintElem                 { $$ = $1; }
+           | ColCheckConstraintElem                { $$ = $1; }
+       ;
+
+/*****************************************************************************
+ *
+ *         QUERY:
+ *             ALTER FORIGN TABLE relname ADD [COLUMN] column-definition;
+ *             ALTER FORIGN TABLE relname DROP [COLUMN] name [CASCADE];
+ *             ALTER FORIGN TABLE relname ALTER [COLUMN] name TYPE type;
+ *             ALTER FORIGN TABLE relname ALTER [COLUMN] name SET [NOT] NULL;
+ *             ALTER FORIGN TABLE relname ADD CONSTRAINT name;
+ *             ALTER FORIGN TABLE relname DROP CONSTRAINT name;
+ *             ALTER FORIGN TABLE relname INHERIT parent;
+ *             ALTER FORIGN TABLE relname OWNER TO role;
+ *             ALTER FORIGN TABLE relname OPTIONS (...);
+ *
+ *****************************************************************************/
+AlterForeignTableStmt:
+           ALTER FOREIGN TABLE relation_expr alter_foreign_table_cmds
+               {
+                   AlterTableStmt *n = makeNode(AlterTableStmt);
+                   n->relation = $4;
+                   n->cmds = $5;
+                   n->relkind = OBJECT_FOREIGN_TABLE;
+                   $$ = (Node *)n;
+               }
+       ;
+
+alter_foreign_table_cmds:
+           alter_foreign_table_cmd                 { $$ = list_make1($1); }
+           | alter_foreign_table_cmds ',' alter_foreign_table_cmd
+                                                   { $$ = lappend($1, $3); }
+       ;
+
+alter_foreign_table_cmd:
+           /* ALTER FOREIGN TABLE <name> ADD <coldef> */
+           ADD_P foreignColumnDef
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_AddColumn;
+                   n->def = $2;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> ADD COLUMN <coldef> */
+           | ADD_P COLUMN foreignColumnDef
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_AddColumn;
+                   n->def = $3;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> DROP NOT NULL */
+           | ALTER opt_column ColId DROP NOT NULL_P
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_DropNotNull;
+                   n->name = $3;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> SET NOT NULL */
+           | ALTER opt_column ColId SET NOT NULL_P
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_SetNotNull;
+                   n->name = $3;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
+           | DROP opt_column IF_P EXISTS ColId opt_drop_behavior
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_DropColumn;
+                   n->name = $5;
+                   n->behavior = $6;
+                   n->missing_ok = TRUE;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> DROP [COLUMN] <colname> [RESTRICT|CASCADE] */
+           | DROP opt_column ColId opt_drop_behavior
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_DropColumn;
+                   n->name = $3;
+                   n->behavior = $4;
+                   n->missing_ok = FALSE;
+                   $$ = (Node *)n;
+               }
+           /*
+            * ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> [SET DATA] TYPE <typename>
+            */
+           | ALTER opt_column ColId opt_set_data TYPE_P Typename
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_AlterColumnType;
+                   n->name = $3;
+                   n->def = (Node *) $6;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> ADD CONSTRAINT ... */
+           | ADD_P ForeignTableConstraint
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_AddConstraint;
+                   n->def = $2;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> DROP CONSTRAINT IF EXISTS <name> [RESTRICT|CASCADE] */
+           | DROP CONSTRAINT IF_P EXISTS name opt_drop_behavior
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_DropConstraint;
+                   n->name = $5;
+                   n->behavior = $6;
+                   n->missing_ok = TRUE;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */
+           | DROP CONSTRAINT name opt_drop_behavior
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_DropConstraint;
+                   n->name = $3;
+                   n->behavior = $4;
+                   n->missing_ok = FALSE;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> INHERIT <parent> */
+           | INHERIT qualified_name
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_AddInherit;
+                   n->def = (Node *) $2;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> NO INHERIT <parent> */
+           | NO INHERIT qualified_name
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_DropInherit;
+                   n->def = (Node *) $3;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> OWNER TO RoleId */
+           | OWNER TO RoleId
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_ChangeOwner;
+                   n->name = $3;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> OPTIONS (...) */
+           | alter_generic_options
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_GenericOptions;
+                   n->def = (Node *)$1;
+                   $$ = (Node *) n;
+               }
+       ;
+
 /*****************************************************************************
  *
  *         QUERY:
@@ -4185,6 +4522,7 @@ drop_type:    TABLE                                   { $$ = OBJECT_TABLE; }
            | TEXT_P SEARCH DICTIONARY              { $$ = OBJECT_TSDICTIONARY; }
            | TEXT_P SEARCH TEMPLATE                { $$ = OBJECT_TSTEMPLATE; }
            | TEXT_P SEARCH CONFIGURATION           { $$ = OBJECT_TSCONFIGURATION; }
+           | FOREIGN TABLE                         { $$ = OBJECT_FOREIGN_TABLE; }
        ;
 
 any_name_list:
@@ -4236,8 +4574,8 @@ opt_restart_seqs:
  *                CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
  *                CAST | COLUMN | SCHEMA | TABLESPACE | ROLE |
  *                TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
- *                TEXT SEARCH TEMPLATE |
- *                TEXT SEARCH CONFIGURATION ] <objname> |
+ *                TEXT SEARCH TEMPLATE | TEXT SEARCH CONFIGURATION |
+ *                FOREIGN TABLE ] <objname> |
  *              AGGREGATE <aggname> (arg1, ...) |
  *              FUNCTION <funcname> (arg1, arg2, ...) |
  *              OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
@@ -4414,6 +4752,7 @@ comment_type:
            | CONVERSION_P                      { $$ = OBJECT_CONVERSION; }
            | TABLESPACE                        { $$ = OBJECT_TABLESPACE; }
            | ROLE                              { $$ = OBJECT_ROLE; }
+           | FOREIGN TABLE                     { $$ = OBJECT_FOREIGN_TABLE; }
        ;
 
 comment_text:
@@ -4830,6 +5169,14 @@ privilege_target:
                    n->objs = $3;
                    $$ = n;
                }
+           | FOREIGN TABLE qualified_name_list
+               {
+                   PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+                   n->targtype = ACL_TARGET_OBJECT;
+                   n->objtype = ACL_OBJECT_FOREIGN_TABLE;
+                   n->objs = $3;
+                   $$ = n;
+               }
            | FUNCTION function_with_argtypes_list
                {
                    PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -5916,6 +6263,15 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
                    n->newname = $6;
                    $$ = (Node *)n;
                }
+           | ALTER FOREIGN TABLE relation_expr RENAME TO name
+               {
+                   RenameStmt *n = makeNode(RenameStmt);
+                   n->renameType = OBJECT_FOREIGN_TABLE;
+                   n->relation = $4;
+                   n->subname = NULL;
+                   n->newname = $7;
+                   $$ = (Node *)n;
+               }
            | ALTER TABLE relation_expr RENAME opt_column name TO name
                {
                    RenameStmt *n = makeNode(RenameStmt);
@@ -5925,6 +6281,15 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
                    n->newname = $8;
                    $$ = (Node *)n;
                }
+           | ALTER FOREIGN TABLE relation_expr RENAME opt_column name TO name
+               {
+                   RenameStmt *n = makeNode(RenameStmt);
+                   n->renameType = OBJECT_COLUMN;
+                   n->relation = $4;
+                   n->subname = $7;
+                   n->newname = $9;
+                   $$ = (Node *)n;
+               }
            | ALTER TRIGGER name ON qualified_name RENAME TO name
                {
                    RenameStmt *n = makeNode(RenameStmt);
@@ -6160,6 +6525,14 @@ AlterObjectSchemaStmt:
                    n->newschema = $6;
                    $$ = (Node *)n;
                }
+           | ALTER FOREIGN TABLE relation_expr SET SCHEMA name
+               {
+                   AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+                   n->objectType = OBJECT_FOREIGN_TABLE;
+                   n->relation = $4;
+                   n->newschema = $7;
+                   $$ = (Node *)n;
+               }
            | ALTER TYPE_P any_name SET SCHEMA name
                {
                    AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
index aa7c144c941ac99295adacc0755d133ecc98bc24..3af5c3868d2dd2382d83b5b6842dc04d49806983 100644 (file)
@@ -65,7 +65,7 @@
 /* State shared by transformCreateStmt and its subroutines */
 typedef struct
 {
-   const char *stmtType;       /* "CREATE TABLE" or "ALTER TABLE" */
+   const char *stmtType;       /* "CREATE [FOREIGN] TABLE" or "ALTER TABLE" */
    RangeVar   *relation;       /* relation to create */
    Relation    rel;            /* opened/locked rel, if ALTER */
    List       *inhRelations;   /* relations to inherit from */
@@ -173,7 +173,10 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
    pstate = make_parsestate(NULL);
    pstate->p_sourcetext = queryString;
 
-   cxt.stmtType = "CREATE TABLE";
+   if (IsA(stmt, CreateForeignServerStmt))
+       cxt.stmtType = "CREATE FOREIGN TABLE";
+   else
+       cxt.stmtType = "CREATE TABLE";
    cxt.relation = stmt->relation;
    cxt.rel = NULL;
    cxt.inhRelations = stmt->inhRelations;
index 803c6036ab8f1a8a538d6e035c04c28da5b4655d..d7b01f7877312c8e14ea6b2056411cf135cb087c 100644 (file)
@@ -220,6 +220,7 @@ check_xact_readonly(Node *parsetree)
        case T_AlterUserMappingStmt:
        case T_DropUserMappingStmt:
        case T_AlterTableSpaceOptionsStmt:
+       case T_CreateForeignTableStmt:
        case T_SecLabelStmt:
            PreventCommandIfReadOnly(CreateCommandTag(parsetree));
            break;
@@ -492,6 +493,7 @@ standard_ProcessUtility(Node *parsetree,
            break;
 
        case T_CreateStmt:
+       case T_CreateForeignTableStmt:
            {
                List       *stmts;
                ListCell   *l;
@@ -540,6 +542,16 @@ standard_ProcessUtility(Node *parsetree,
 
                        AlterTableCreateToastTable(relOid, toast_options);
                    }
+                   else if (IsA(stmt, CreateForeignTableStmt))
+                   {
+                       /* Create the table itself */
+                       relOid = DefineRelation((CreateStmt *) stmt,
+                                               RELKIND_FOREIGN_TABLE,
+                                               InvalidOid);
+
+                       CreateForeignTable((CreateForeignTableStmt *) stmt,
+                                               relOid);
+                   }
                    else
                    {
                        /* Recurse for anything else */
@@ -618,6 +630,7 @@ standard_ProcessUtility(Node *parsetree,
                    case OBJECT_SEQUENCE:
                    case OBJECT_VIEW:
                    case OBJECT_INDEX:
+                   case OBJECT_FOREIGN_TABLE:
                        RemoveRelations(stmt);
                        break;
 
@@ -1557,6 +1570,10 @@ CreateCommandTag(Node *parsetree)
            tag = "DROP USER MAPPING";
            break;
 
+       case T_CreateForeignTableStmt:
+           tag = "CREATE FOREIGN TABLE";
+           break;
+
        case T_DropStmt:
            switch (((DropStmt *) parsetree)->removeType)
            {
@@ -1596,6 +1613,9 @@ CreateCommandTag(Node *parsetree)
                case OBJECT_TSCONFIGURATION:
                    tag = "DROP TEXT SEARCH CONFIGURATION";
                    break;
+               case OBJECT_FOREIGN_TABLE:
+                   tag = "DROP FOREIGN TABLE";
+                   break;
                default:
                    tag = "???";
            }
@@ -1666,6 +1686,9 @@ CreateCommandTag(Node *parsetree)
                case OBJECT_VIEW:
                    tag = "ALTER VIEW";
                    break;
+               case OBJECT_FOREIGN_TABLE:
+                   tag = "ALTER FOREIGN TABLE";
+                   break;
                case OBJECT_TSPARSER:
                    tag = "ALTER TEXT SEARCH PARSER";
                    break;
@@ -1736,6 +1759,9 @@ CreateCommandTag(Node *parsetree)
                case OBJECT_VIEW:
                    tag = "ALTER VIEW";
                    break;
+               case OBJECT_FOREIGN_TABLE:
+                   tag = "ALTER FOREIGN TABLE";
+                   break;
                default:
                    tag = "???";
                    break;
@@ -1796,6 +1822,9 @@ CreateCommandTag(Node *parsetree)
                case OBJECT_FOREIGN_SERVER:
                    tag = "ALTER SERVER";
                    break;
+               case OBJECT_FOREIGN_TABLE:
+                   tag = "ALTER FOREIGN TABLE";
+                   break;
                default:
                    tag = "???";
                    break;
@@ -1820,6 +1849,9 @@ CreateCommandTag(Node *parsetree)
                case OBJECT_VIEW:
                    tag = "ALTER VIEW";
                    break;
+               case OBJECT_FOREIGN_TABLE:
+                   tag = "ALTER FOREIGN TABLE";
+                   break;
                default:
                    tag = "???";
                    break;
@@ -2316,6 +2348,7 @@ GetCommandLogLevel(Node *parsetree)
            break;
 
        case T_CreateStmt:
+       case T_CreateForeignTableStmt:
            lev = LOGSTMT_DDL;
            break;
 
index e2af0592c50c9a539cec695fe7a2cac35d1ec278..6f9b925ca1782befe13fbdbc9b17df55b5fcb52a 100644 (file)
@@ -782,6 +782,10 @@ acldefault(GrantObjectType objtype, Oid ownerId)
            world_default = ACL_NO_RIGHTS;
            owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
            break;
+       case ACL_OBJECT_FOREIGN_TABLE:
+           world_default = ACL_NO_RIGHTS;
+           owner_default = ACL_ALL_RIGHTS_FOREIGN_TABLE;
+           break;
        default:
            elog(ERROR, "unrecognized objtype: %d", (int) objtype);
            world_default = ACL_NO_RIGHTS;      /* keep compiler quiet */
index dd125924731e06f2cb8469c1d4c45a0a37751e6f..a08af41413b59502ff7c1b36b616b0318f7d57fe 100644 (file)
@@ -453,3 +453,29 @@ pg_node_tree_send(PG_FUNCTION_ARGS)
 {
    return textsend(fcinfo);
 }
+
+/*
+ * fdw_handler_in      - input routine for pseudo-type FDW_HANDLER.
+ */
+Datum
+fdw_handler_in(PG_FUNCTION_ARGS)
+{
+   ereport(ERROR,
+           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+            errmsg("cannot accept a value of type fdw_handler")));
+
+   PG_RETURN_VOID();           /* keep compiler quiet */
+}
+
+/*
+ * fdw_handler_out     - output routine for pseudo-type FDW_HANDLER.
+ */
+Datum
+fdw_handler_out(PG_FUNCTION_ARGS)
+{
+   ereport(ERROR,
+           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+            errmsg("cannot display a value of type fdw_handler")));
+
+   PG_RETURN_VOID();           /* keep compiler quiet */
+}
index 08a14431b161dd54f7ceaabf55cca221d6655ac2..12297fa52a738554999442767fa3a0bdfed75bb5 100644 (file)
@@ -35,6 +35,7 @@
 #include "catalog/pg_enum.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
@@ -398,6 +399,17 @@ static const struct cachedesc cacheinfo[] = {
        },
        32
    },
+   {ForeignTableRelationId,        /* FOREIGNTABLEREL */
+       ForeignTableRelidIndexId,
+       1,
+       {
+           Anum_pg_foreign_table_ftrelid,
+           0,
+           0,
+           0
+       },
+       128
+   },
    {IndexRelationId,           /* INDEXRELID */
        IndexRelidIndexId,
        1,
index 14ed9147a19c3620ced5e0efc9e6d7ea59d2ad7e..380162bace88f028bd5e5441f743df703e52f316 100644 (file)
@@ -30,6 +30,7 @@
 #endif
 
 #include "catalog/pg_authid.h"
+#include "foreign/foreign.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
@@ -379,6 +380,7 @@ SetUserIdAndContext(Oid userid, bool sec_def_context)
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("cannot set parameter \"%s\" within security-restricted operation",
                        "role")));
+
    CurrentUserId = userid;
    if (sec_def_context)
        SecurityRestrictionContext |= SECURITY_LOCAL_USERID_CHANGE;
index e4a02014fd073233ec4dfbf29ce3d08dffbf53e6..6451329639a502fded834cf89b1393369ed06421 100644 (file)
@@ -247,7 +247,7 @@ flagInhTables(TableInfo *tblinfo, int numTables,
 
    for (i = 0; i < numTables; i++)
    {
-       /* Sequences and views never have parents */
+       /* Sequences and views and foreign tables never have parents */
        if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
            tblinfo[i].relkind == RELKIND_VIEW)
            continue;
index d53e908a44048a1924afec7f2619a3914e7f10e5..d96a6b035f43e99611d62ffb6a568d98cba53119 100644 (file)
@@ -868,6 +868,8 @@ do { \
        CONVERT_PRIV('U', "USAGE");
    else if (strcmp(type, "FOREIGN SERVER") == 0)
        CONVERT_PRIV('U', "USAGE");
+   else if (strcmp(type, "FOREIGN TABLE") == 0)
+       CONVERT_PRIV('r', "SELECT");
    else if (strcmp(type, "LARGE OBJECT") == 0)
    {
        CONVERT_PRIV('r', "SELECT");
index e1f3b055deffea5e3019e571532a42bbfb89884f..64d8d93dda81805540ac70dbd7afcf11390c19fc 100644 (file)
@@ -2726,6 +2726,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
        strcmp(type, "DOMAIN") == 0 ||
        strcmp(type, "TABLE") == 0 ||
        strcmp(type, "TYPE") == 0 ||
+       strcmp(type, "FOREIGN TABLE") == 0 ||
        strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
        strcmp(type, "TEXT SEARCH CONFIGURATION") == 0)
    {
@@ -2918,6 +2919,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
            strcmp(te->desc, "TYPE") == 0 ||
            strcmp(te->desc, "VIEW") == 0 ||
            strcmp(te->desc, "SEQUENCE") == 0 ||
+           strcmp(te->desc, "FOREIGN TABLE") == 0 ||
            strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
            strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0 ||
            strcmp(te->desc, "FOREIGN DATA WRAPPER") == 0 ||
index 66274b442e6eb7aedab60bf23c03795c02d9b182..8f7ece479b80b754bff8a08e6f6111ccafa8181e 100644 (file)
@@ -970,8 +970,9 @@ expand_table_name_patterns(SimpleStringList *patterns, SimpleOidList *oids)
                          "SELECT c.oid"
                          "\nFROM pg_catalog.pg_class c"
        "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace"
-                         "\nWHERE c.relkind in ('%c', '%c', '%c')\n",
-                         RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW);
+                         "\nWHERE c.relkind in ('%c', '%c', '%c', '%c')\n",
+                         RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
+                         RELKIND_FOREIGN_TABLE);
        processSQLNamePattern(g_conn, query, cell->val, true, false,
                              "n.nspname", "c.relname", NULL,
                              "pg_catalog.pg_table_is_visible(c.oid)");
@@ -1471,6 +1472,9 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids)
        /* Skip SEQUENCEs (handled elsewhere) */
        if (tblinfo[i].relkind == RELKIND_SEQUENCE)
            continue;
+       /* Skip FOREIGN TABLEs (no data to dump) */
+       if (tblinfo[i].relkind == RELKIND_FOREIGN_TABLE)
+           continue;
 
        if (tblinfo[i].dobj.dump)
        {
@@ -3477,7 +3481,41 @@ getTables(int *numTables)
     * we cannot correctly identify inherited columns, owned sequences, etc.
     */
 
-   if (g_fout->remoteVersion >= 90000)
+   if (g_fout->remoteVersion >= 90100)
+   {
+       /*
+        * Left join to pick up dependency info linking sequences to their
+        * owning column, if any (note this dependency is AUTO as of 8.2)
+        */
+       appendPQExpBuffer(query,
+                         "SELECT c.tableoid, c.oid, c.relname, "
+                         "c.relacl, c.relkind, c.relnamespace, "
+                         "(%s c.relowner) AS rolname, "
+                         "c.relchecks, c.relhastriggers, "
+                         "c.relhasindex, c.relhasrules, c.relhasoids, "
+                         "c.relfrozenxid, "
+                         "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
+                         "d.refobjid AS owning_tab, "
+                         "d.refobjsubid AS owning_col, "
+                         "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
+                       "array_to_string(c.reloptions, ', ') AS reloptions, "
+                         "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+                         "FROM pg_class c "
+                         "LEFT JOIN pg_depend d ON "
+                         "(c.relkind = '%c' AND "
+                         "d.classid = c.tableoid AND d.objid = c.oid AND "
+                         "d.objsubid = 0 AND "
+                         "d.refclassid = c.tableoid AND d.deptype = 'a') "
+                      "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
+                         "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') "
+                         "ORDER BY c.oid",
+                         username_subquery,
+                         RELKIND_SEQUENCE,
+                         RELKIND_RELATION, RELKIND_SEQUENCE,
+                         RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
+                         RELKIND_FOREIGN_TABLE);
+   }
+   else if (g_fout->remoteVersion >= 90000)
    {
        /*
         * Left join to pick up dependency info linking sequences to their
@@ -3829,7 +3867,8 @@ getTables(int *numTables)
         * NOTE: it'd be kinda nice to lock views and sequences too, not only
         * plain tables, but the backend doesn't presently allow that.
         */
-       if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION)
+       if (tblinfo[i].dobj.dump && (tblinfo[i].relkind == RELKIND_RELATION ||
+           tblinfo[i].relkind == RELKIND_FOREIGN_TABLE))
        {
            resetPQExpBuffer(query);
            appendPQExpBuffer(query,
@@ -5886,6 +5925,7 @@ getForeignDataWrappers(int *numForeignDataWrappers)
    int         i_fdwname;
    int         i_rolname;
    int         i_fdwvalidator;
+   int         i_fdwhandler;
    int         i_fdwacl;
    int         i_fdwoptions;
 
@@ -5900,7 +5940,8 @@ getForeignDataWrappers(int *numForeignDataWrappers)
    selectSourceSchema("pg_catalog");
 
    appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
-       "(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, fdwacl,"
+       "(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, "
+       "fdwhandler::pg_catalog.regproc, fdwacl,"
                      "array_to_string(ARRAY("
         "      SELECT option_name || ' ' || quote_literal(option_value) "
       "        FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
@@ -5920,6 +5961,7 @@ getForeignDataWrappers(int *numForeignDataWrappers)
    i_fdwname = PQfnumber(res, "fdwname");
    i_rolname = PQfnumber(res, "rolname");
    i_fdwvalidator = PQfnumber(res, "fdwvalidator");
+   i_fdwhandler = PQfnumber(res, "fdwhandler");
    i_fdwacl = PQfnumber(res, "fdwacl");
    i_fdwoptions = PQfnumber(res, "fdwoptions");
 
@@ -5933,6 +5975,7 @@ getForeignDataWrappers(int *numForeignDataWrappers)
        fdwinfo[i].dobj.namespace = NULL;
        fdwinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
        fdwinfo[i].fdwvalidator = strdup(PQgetvalue(res, i, i_fdwvalidator));
+       fdwinfo[i].fdwhandler = strdup(PQgetvalue(res, i, i_fdwhandler));
        fdwinfo[i].fdwoptions = strdup(PQgetvalue(res, i, i_fdwoptions));
        fdwinfo[i].fdwacl = strdup(PQgetvalue(res, i, i_fdwacl));
 
@@ -10238,6 +10281,10 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo)
        appendPQExpBuffer(q, " VALIDATOR %s",
                          fdwinfo->fdwvalidator);
 
+   if (fdwinfo->fdwhandler && strcmp(fdwinfo->fdwhandler, "-") != 0)
+       appendPQExpBuffer(q, " HANDLER %s",
+                         fdwinfo->fdwhandler);
+
    if (fdwinfo->fdwoptions && strlen(fdwinfo->fdwoptions) > 0)
        appendPQExpBuffer(q, " OPTIONS (%s)", fdwinfo->fdwoptions);
 
@@ -10893,7 +10940,9 @@ dumpTable(Archive *fout, TableInfo *tbinfo)
        /* Handle the ACL here */
        namecopy = strdup(fmtId(tbinfo->dobj.name));
        dumpACL(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
-               (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE",
+               (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" :
+               (tbinfo->relkind == RELKIND_FOREIGN_TABLE) ? "FOREIGN TABLE" :
+               "TABLE",
                namecopy, NULL, tbinfo->dobj.name,
                tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
                tbinfo->relacl);
@@ -10962,6 +11011,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
    int         j,
                k;
    bool        toast_set = false;
+   char       *srvname;
+   char       *ftoptions = NULL;
 
    /* Make sure we are in proper schema */
    selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
@@ -11035,7 +11086,35 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
    }
    else
    {
-       reltypename = "TABLE";
+       if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+       {
+           int     i_srvname;
+           int     i_ftoptions;
+
+           reltypename = "FOREIGN TABLE";
+
+           /* retrieve name of foreign server and generic options */ 
+           appendPQExpBuffer(query,
+               "SELECT fs.srvname, array_to_string(ARRAY("
+               "   SELECT option_name || ' ' || quote_literal(option_value)"
+               "   FROM pg_options_to_table(ftoptions)), ', ') AS ftoptions "
+               "FROM pg_foreign_table ft JOIN pg_foreign_server fs "
+               "   ON (fs.oid = ft.ftserver) "
+               "WHERE ft.ftrelid = %u", tbinfo->dobj.catId.oid);
+           res = PQexec(g_conn, query->data);
+           check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+           i_srvname = PQfnumber(res, "srvname");
+           i_ftoptions = PQfnumber(res, "ftoptions");
+           srvname = strdup(PQgetvalue(res, 0, i_srvname));
+           ftoptions = strdup(PQgetvalue(res, 0, i_ftoptions));
+           PQclear(res);
+       }
+       else
+       {
+           reltypename = "TABLE";
+           srvname = NULL;
+           ftoptions = NULL;
+       }
        numParents = tbinfo->numParents;
        parents = tbinfo->parents;
 
@@ -11043,7 +11122,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
         * DROP must be fully qualified in case same name appears in
         * pg_catalog
         */
-       appendPQExpBuffer(delq, "DROP TABLE %s.",
+       appendPQExpBuffer(delq, "DROP %s %s.", reltypename,
                          fmtId(tbinfo->dobj.namespace->dobj.name));
        appendPQExpBuffer(delq, "%s;\n",
                          fmtId(tbinfo->dobj.name));
@@ -11051,7 +11130,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
        if (binary_upgrade)
            binary_upgrade_set_relfilenodes(q, tbinfo->dobj.catId.oid, false);
 
-       appendPQExpBuffer(q, "CREATE TABLE %s",
+       appendPQExpBuffer(q, "CREATE %s %s", reltypename,
                          fmtId(tbinfo->dobj.name));
        if (tbinfo->reloftype)
            appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
@@ -11186,6 +11265,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
            appendPQExpBuffer(q, ")");
        }
 
+       if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+           appendPQExpBuffer(q, "\nSERVER %s", srvname);
+
        if ((tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) ||
          (tbinfo->toast_reloptions && strlen(tbinfo->toast_reloptions) > 0))
        {
@@ -11205,6 +11287,10 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
            appendPQExpBuffer(q, ")");
        }
 
+       /* Dump generic options if any */
+       if (ftoptions && ftoptions[0])
+           appendPQExpBuffer(q, "\nOPTIONS (%s)", ftoptions);
+
        appendPQExpBuffer(q, ";\n");
 
        /*
@@ -11219,7 +11305,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
         * order.  That also means we have to take care about setting
         * attislocal correctly, plus fix up any inherited CHECK constraints.
         */
-       if (binary_upgrade)
+       if (binary_upgrade && tbinfo->relkind == RELKIND_RELATION)
        {
            for (j = 0; j < tbinfo->numatts; j++)
            {
index 78855357c88a1bf678613e8911914cf0f7372a38..5905d0f00c661f427b14abbdbea347b596bea8e7 100644 (file)
@@ -420,6 +420,7 @@ typedef struct _fdwInfo
    DumpableObject dobj;
    char       *rolname;
    char       *fdwvalidator;
+   char       *fdwhandler;
    char       *fdwoptions;
    char       *fdwacl;
 } FdwInfo;
index c1edf44a6071d63c3bfb50515cf2f0b5b2cc04c2..a05af3925d4cc177b83a07a6434b369869bead23 100644 (file)
@@ -369,7 +369,7 @@ exec_command(const char *cmd,
                    success = describeTableDetails(pattern, show_verbose, show_system);
                else
                    /* standard listing of interesting things */
-                   success = listTables("tvs", NULL, show_verbose, show_system);
+                   success = listTables("tvsE", NULL, show_verbose, show_system);
                break;
            case 'a':
                success = describeAggregates(pattern, show_verbose, show_system);
@@ -432,6 +432,7 @@ exec_command(const char *cmd,
            case 'v':
            case 'i':
            case 's':
+           case 'E':
                success = listTables(&cmd[1], pattern, show_verbose, show_system);
                break;
            case 'r':
@@ -483,6 +484,9 @@ exec_command(const char *cmd,
                    case 'w':
                        success = listForeignDataWrappers(pattern, show_verbose);
                        break;
+                   case 't':
+                       success = listForeignTables(pattern, show_verbose);
+                       break;
                    default:
                        status = PSQL_CMD_UNKNOWN;
                        break;
index c4370a1dd39c8133cf509edad1f46a9c0b0fc9a6..01a323d2040b4a54bafeca7d8dd08cfc7b07d8a4 100644 (file)
@@ -687,11 +687,12 @@ permissionsList(const char *pattern)
    printfPQExpBuffer(&buf,
                      "SELECT n.nspname as \"%s\",\n"
                      "  c.relname as \"%s\",\n"
-                     "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' END as \"%s\",\n"
+                     "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
                      "  ",
                      gettext_noop("Schema"),
                      gettext_noop("Name"),
       gettext_noop("table"), gettext_noop("view"), gettext_noop("sequence"),
+      gettext_noop("foreign table"),
                      gettext_noop("Type"));
 
    printACLColumn(&buf, "c.relacl");
@@ -707,7 +708,7 @@ permissionsList(const char *pattern)
 
    appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_class c\n"
       "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
-                     "WHERE c.relkind IN ('r', 'v', 'S')\n");
+                     "WHERE c.relkind IN ('r', 'v', 'S', 'f')\n");
 
    /*
     * Unless a schema pattern is specified, we suppress system and temp
@@ -921,15 +922,16 @@ objectDescription(const char *pattern, bool showSystem)
                      "  n.nspname as nspname,\n"
                      "  CAST(c.relname AS pg_catalog.text) as name,\n"
                      "  CAST(\n"
-                     "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' END"
+                     "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END"
                      "  AS pg_catalog.text) as object\n"
                      "  FROM pg_catalog.pg_class c\n"
     "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
-                     "  WHERE c.relkind IN ('r', 'v', 'i', 'S')\n",
+                     "  WHERE c.relkind IN ('r', 'v', 'i', 'S', 'f')\n",
                      gettext_noop("table"),
                      gettext_noop("view"),
                      gettext_noop("index"),
-                     gettext_noop("sequence"));
+                     gettext_noop("sequence"),
+                     gettext_noop("foreign table"));
 
    if (!showSystem && !pattern)
        appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
@@ -1297,6 +1299,10 @@ describeOneTableDetails(const char *schemaname,
            printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
                              schemaname, relationname);
            break;
+       case 'f':
+           printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
+                             schemaname, relationname);
+           break;
        default:
            /* untranslated unknown relkind */
            printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
@@ -1309,7 +1315,8 @@ describeOneTableDetails(const char *schemaname,
    headers[1] = gettext_noop("Type");
    cols = 2;
 
-   if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v')
+   if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
+       tableinfo.relkind == 'f')
    {
        show_modifiers = true;
        headers[cols++] = gettext_noop("Modifiers");
@@ -1537,7 +1544,7 @@ describeOneTableDetails(const char *schemaname,
            PQclear(result);
        }
    }
-   else if (tableinfo.relkind == 'r')
+   else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f')
    {
        /* Footer information about a table */
        PGresult   *result = NULL;
@@ -1956,11 +1963,36 @@ describeOneTableDetails(const char *schemaname,
    /*
     * Finish printing the footer information about a table.
     */
-   if (tableinfo.relkind == 'r')
+   if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f')
    {
        PGresult   *result;
        int         tuples;
 
+       /* print foreign server name */
+       if (tableinfo.relkind == 'f')
+       {
+           /* Footer information about foreign table */
+           printfPQExpBuffer(&buf,
+                             "SELECT s.srvname\n"
+                             "FROM pg_catalog.pg_foreign_table f,\n"
+                             "     pg_catalog.pg_foreign_server s\n"
+                             "WHERE f.ftrelid = %s AND s.oid = f.ftserver",
+                             oid);
+           result = PSQLexec(buf.data, false);
+           if (!result)
+               goto error_return;
+           else if (PQntuples(result) != 1)
+           {
+               PQclear(result);
+               goto error_return;
+           }
+
+           printfPQExpBuffer(&buf, "Server: %s",
+               PQgetvalue(result, 0, 0));
+           printTableAddFooter(&cont, buf.data);
+           PQclear(result);
+       }
+
        /* print inherited tables */
        printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid);
 
@@ -2370,8 +2402,9 @@ listDbRoleSettings(const char *pattern, const char *pattern2)
  * i - indexes
  * v - views
  * s - sequences
+ * E - foreign table (Note: different from 'f', the relkind value)
  * (any order of the above is fine)
- * If tabtypes is empty, we default to \dtvs.
+ * If tabtypes is empty, we default to \dtvsE.
  */
 bool
 listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
@@ -2380,14 +2413,15 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
    bool        showIndexes = strchr(tabtypes, 'i') != NULL;
    bool        showViews = strchr(tabtypes, 'v') != NULL;
    bool        showSeq = strchr(tabtypes, 's') != NULL;
+   bool        showForeign = strchr(tabtypes, 'E') != NULL;
 
    PQExpBufferData buf;
    PGresult   *res;
    printQueryOpt myopt = pset.popt;
    static const bool translate_columns[] = {false, false, true, false, false, false, false};
 
-   if (!(showTables || showIndexes || showViews || showSeq))
-       showTables = showViews = showSeq = true;
+   if (!(showTables || showIndexes || showViews || showSeq || showForeign))
+       showTables = showViews = showSeq = showForeign = true;
 
    initPQExpBuffer(&buf);
 
@@ -2398,7 +2432,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
    printfPQExpBuffer(&buf,
                      "SELECT n.nspname as \"%s\",\n"
                      "  c.relname as \"%s\",\n"
-                     "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' END as \"%s\",\n"
+                     "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
                      "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
                      gettext_noop("Schema"),
                      gettext_noop("Name"),
@@ -2407,6 +2441,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
                      gettext_noop("index"),
                      gettext_noop("sequence"),
                      gettext_noop("special"),
+                     gettext_noop("foreign table"),
                      gettext_noop("Type"),
                      gettext_noop("Owner"));
 
@@ -2444,6 +2479,9 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
    if (showSystem || pattern)
        appendPQExpBuffer(&buf, "'s',");        /* was RELKIND_SPECIAL in <=
                                                 * 8.1 */
+   if (showForeign)
+       appendPQExpBuffer(&buf, "'f',");
+
    appendPQExpBuffer(&buf, "''");      /* dummy */
    appendPQExpBuffer(&buf, ")\n");
 
@@ -3338,9 +3376,11 @@ listForeignDataWrappers(const char *pattern, bool verbose)
    printfPQExpBuffer(&buf,
                      "SELECT fdwname AS \"%s\",\n"
                      "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n"
+                     "  fdwhandler::pg_catalog.regproc AS \"%s\",\n"
                      "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
                      gettext_noop("Name"),
                      gettext_noop("Owner"),
+                     gettext_noop("Handler"),
                      gettext_noop("Validator"));
 
    if (verbose)
@@ -3493,6 +3533,67 @@ listUserMappings(const char *pattern, bool verbose)
    return true;
 }
 
+/*
+ * \det
+ *
+ * Describes foreign tables.
+ */
+bool
+listForeignTables(const char *pattern, bool verbose)
+{
+   PQExpBufferData buf;
+   PGresult   *res;
+   printQueryOpt myopt = pset.popt;
+
+   if (pset.sversion < 90100)
+   {
+       fprintf(stderr, _("The server (version %d.%d) does not support foreign table.\n"),
+               pset.sversion / 10000, (pset.sversion / 100) % 100);
+       return true;
+   }
+
+   initPQExpBuffer(&buf);
+   printfPQExpBuffer(&buf,
+                     "SELECT n.nspname AS \"%s\",\n"
+                     "  c.relname AS \"%s\",\n"
+                     "  s.srvname AS \"%s\"",
+                     gettext_noop("Schema"),
+                     gettext_noop("Table"),
+                     gettext_noop("Server"));
+
+   if (verbose)
+       appendPQExpBuffer(&buf,
+                         ",\n  ft.ftoptions AS \"%s\"",
+                         gettext_noop("Options"));
+
+   appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
+   appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
+   appendPQExpBuffer(&buf, "\n pg_catalog.pg_namespace n,");
+   appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s\n");
+   appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
+   appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
+   appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
+
+   processSQLNamePattern(pset.db, &buf, pattern, true, false,
+                         NULL, "n.nspname", "c.relname", NULL);
+
+   appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+   res = PSQLexec(buf.data, false);
+   termPQExpBuffer(&buf);
+   if (!res)
+       return false;
+
+   myopt.nullPrint = NULL;
+   myopt.title = _("List of foreign tables");
+   myopt.translate_header = true;
+
+   printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+   PQclear(res);
+   return true;
+}
+
 /*
  * printACLColumn
  *
index 6a6abdba471fd5e613b799b2f6c28834ca9580aa..486babc811b648b8511f00b0584c6d3944d4a3ac 100644 (file)
@@ -81,5 +81,8 @@ extern bool listForeignServers(const char *pattern, bool verbose);
 /* \deu */
 extern bool listUserMappings(const char *pattern, bool verbose);
 
+/* \det */
+extern bool listForeignTables(const char *pattern, bool verbose);
+
 
 #endif   /* DESCRIBE_H */
index 4458d631ab69aa8457fb1b8fe1fb6e4f7b8e1f51..463438dbf4063bf6f7947c674a652d62ae3058a6 100644 (file)
@@ -158,7 +158,7 @@ slashUsage(unsigned short int pager)
 {
    FILE       *output;
 
-   output = PageOutput(90, pager);
+   output = PageOutput(92, pager);
 
    /* if you add/remove a line here, change the row count above */
 
@@ -199,6 +199,7 @@ slashUsage(unsigned short int pager)
    fprintf(output, _("  \\dd[S]  [PATTERN]      show comments on objects\n"));
    fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
    fprintf(output, _("  \\dD[S]  [PATTERN]      list domains\n"));
+   fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));
    fprintf(output, _("  \\des[+] [PATTERN]      list foreign servers\n"));
    fprintf(output, _("  \\deu[+] [PATTERN]      list user mappings\n"));
    fprintf(output, _("  \\dew[+] [PATTERN]      list foreign-data wrappers\n"));
@@ -219,6 +220,7 @@ slashUsage(unsigned short int pager)
    fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
    fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
    fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
+   fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
    fprintf(output, _("  \\l[+]                  list all databases\n"));
    fprintf(output, _("  \\sf[+] FUNCNAME        show a function's definition\n"));
    fprintf(output, _("  \\z      [PATTERN]      same as \\dp\n"));
index c88d671f40dab53c3836255852f191b377735176..f169576c37bdd23a820e6be019b75a5005c66c35 100644 (file)
@@ -288,6 +288,21 @@ static const SchemaQuery Query_for_list_of_sequences = {
    NULL
 };
 
+static const SchemaQuery Query_for_list_of_foreign_tables = {
+   /* catname */
+   "pg_catalog.pg_class c",
+   /* selcondition */
+   "c.relkind IN ('f')",
+   /* viscondition */
+   "pg_catalog.pg_table_is_visible(c.oid)",
+   /* namespace */
+   "c.relnamespace",
+   /* result */
+   "pg_catalog.quote_ident(c.relname)",
+   /* qualresult */
+   NULL
+};
+
 static const SchemaQuery Query_for_list_of_tables = {
    /* catname */
    "pg_catalog.pg_class c",
@@ -354,11 +369,11 @@ static const SchemaQuery Query_for_list_of_updatables = {
    NULL
 };
 
-static const SchemaQuery Query_for_list_of_tisv = {
+static const SchemaQuery Query_for_list_of_tisvf = {
    /* catname */
    "pg_catalog.pg_class c",
    /* selcondition */
-   "c.relkind IN ('r', 'i', 'S', 'v')",
+   "c.relkind IN ('r', 'i', 'S', 'v', 'f')",
    /* viscondition */
    "pg_catalog.pg_table_is_visible(c.oid)",
    /* namespace */
@@ -369,11 +384,11 @@ static const SchemaQuery Query_for_list_of_tisv = {
    NULL
 };
 
-static const SchemaQuery Query_for_list_of_tsv = {
+static const SchemaQuery Query_for_list_of_tsvf = {
    /* catname */
    "pg_catalog.pg_class c",
    /* selcondition */
-   "c.relkind IN ('r', 'S', 'v')",
+   "c.relkind IN ('r', 'S', 'v', 'f')",
    /* viscondition */
    "pg_catalog.pg_table_is_visible(c.oid)",
    /* namespace */
@@ -592,6 +607,7 @@ static const pgsql_thing_t words_after_create[] = {
    {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
    {"DOMAIN", NULL, &Query_for_list_of_domains},
    {"FOREIGN DATA WRAPPER", NULL, NULL},
+   {"FOREIGN TABLE", NULL, NULL},
    {"FUNCTION", NULL, &Query_for_list_of_functions},
    {"GROUP", Query_for_list_of_roles},
    {"LANGUAGE", Query_for_list_of_languages},
@@ -696,7 +712,7 @@ psql_completion(char *text, int start, int end)
 
    static const char *const backslash_commands[] = {
        "\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright",
-       "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df",
+       "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\df",
        "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
        "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
        "\\e", "\\echo", "\\ef", "\\encoding",
@@ -759,7 +775,7 @@ psql_completion(char *text, int start, int end)
             pg_strcasecmp(prev3_wd, "TABLE") != 0)
    {
        static const char *const list_ALTER[] =
-       {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
+       {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
            "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
        "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
 
@@ -822,6 +838,16 @@ psql_completion(char *text, int start, int end)
        COMPLETE_WITH_LIST(list_ALTERDATABASE);
    }
 
+   /* ALTER FOREIGN */
+   else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
+            pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+   {
+       static const char *const list_ALTER_FOREIGN[] =
+       {"DATA WRAPPER", "TABLE", NULL};
+
+       COMPLETE_WITH_LIST(list_ALTER_FOREIGN);
+   }
+
    /* ALTER FOREIGN DATA WRAPPER <name> */
    else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
             pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
@@ -834,6 +860,18 @@ psql_completion(char *text, int start, int end)
        COMPLETE_WITH_LIST(list_ALTER_FDW);
    }
 
+   /* ALTER FOREIGN TABLE <name> */
+   else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+            pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
+            pg_strcasecmp(prev2_wd, "TABLE") == 0)
+   {
+       static const char *const list_ALTER_FOREIGN_TABLE[] =
+       {"ADD", "ALTER", "DISABLE", "DROP", "ENABLE", "INHERIT",
+       "NO INHERIT", "RENAME", "RESET", "OWNER TO", NULL};
+
+       COMPLETE_WITH_LIST(list_ALTER_FOREIGN_TABLE);
+   }
+
    /* ALTER INDEX <name> */
    else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
             pg_strcasecmp(prev2_wd, "INDEX") == 0)
@@ -1448,7 +1486,7 @@ psql_completion(char *text, int start, int end)
             pg_strcasecmp(prev_wd, "ON") == 0)
    {
        static const char *const list_COMMENT[] =
-       {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
+       {"CAST", "CONVERSION", "DATABASE", "FOREIGN TABLE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
            "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
            "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
        "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
@@ -1541,6 +1579,16 @@ psql_completion(char *text, int start, int end)
             pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
        COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
 
+   /* CREATE FOREIGN */
+   else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
+            pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+   {
+       static const char *const list_CREATE_FOREIGN[] =
+       {"DATA WRAPPER", "TABLE", NULL};
+
+       COMPLETE_WITH_LIST(list_CREATE_FOREIGN);
+   }
+
    /* CREATE FOREIGN DATA WRAPPER */
    else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
             pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
@@ -1548,6 +1596,18 @@ psql_completion(char *text, int start, int end)
             pg_strcasecmp(prev2_wd, "WRAPPER") == 0)
        COMPLETE_WITH_CONST("VALIDATOR");
 
+   /* CREATE FOREIGN TABLE name (...) */
+   else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
+            pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
+            pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
+            prev_wd[strlen(prev_wd) - 1] == ')')
+   {
+       static const char *const list_CREATE_FOREIGN_TABLE[] =
+       {"INHERTIS", "SERVER", NULL};
+
+       COMPLETE_WITH_LIST(list_CREATE_FOREIGN_TABLE);
+   }
+
    /* CREATE INDEX */
    /* First off we complete CREATE UNIQUE with "INDEX" */
    else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
@@ -1911,6 +1971,14 @@ psql_completion(char *text, int start, int end)
            COMPLETE_WITH_LIST(list_DROPCR);
        }
    }
+   else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
+            pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+   {
+       static const char *const drop_CREATE_FOREIGN[] =
+       {"DATA WRAPPER", "TABLE", NULL};
+
+       COMPLETE_WITH_LIST(drop_CREATE_FOREIGN);
+   }
    else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
             (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
              pg_strcasecmp(prev3_wd, "FUNCTION") == 0) &&
@@ -2015,6 +2083,12 @@ psql_completion(char *text, int start, int end)
             pg_strcasecmp(prev_wd, "WRAPPER") == 0)
        COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
 
+/* FOREIGN TABLE */
+   else if (pg_strcasecmp(prev3_wd, "CREATE") != 0 &&
+            pg_strcasecmp(prev2_wd, "FOREIGN") == 0 &&
+            pg_strcasecmp(prev_wd, "TABLE") == 0)
+        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
+
 /* GRANT && REVOKE */
    /* Complete GRANT/REVOKE with a list of privileges */
    else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
@@ -2046,10 +2120,11 @@ psql_completion(char *text, int start, int end)
    else if ((pg_strcasecmp(prev3_wd, "GRANT") == 0 ||
              pg_strcasecmp(prev3_wd, "REVOKE") == 0) &&
             pg_strcasecmp(prev_wd, "ON") == 0)
-       COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv,
+       COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf,
                                   " UNION SELECT 'DATABASE'"
                                   " UNION SELECT 'FOREIGN DATA WRAPPER'"
                                   " UNION SELECT 'FOREIGN SERVER'"
+                                  " UNION SELECT 'FOREIGN TABLE'"
                                   " UNION SELECT 'FUNCTION'"
                                   " UNION SELECT 'LANGUAGE'"
                                   " UNION SELECT 'LARGE OBJECT'"
@@ -2061,7 +2136,7 @@ psql_completion(char *text, int start, int end)
             pg_strcasecmp(prev_wd, "FOREIGN") == 0)
    {
        static const char *const list_privilege_foreign[] =
-       {"DATA WRAPPER", "SERVER", NULL};
+       {"DATA WRAPPER", "SERVER", "TABLE", NULL};
 
        COMPLETE_WITH_LIST(list_privilege_foreign);
    }
@@ -2582,7 +2657,7 @@ psql_completion(char *text, int start, int end)
    else if (pg_strcasecmp(prev_wd, "FROM") == 0 &&
             pg_strcasecmp(prev3_wd, "COPY") != 0 &&
             pg_strcasecmp(prev3_wd, "\\copy") != 0)
-       COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+       COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf, NULL);
 
 /* Backslash commands */
 /* TODO:  \dc \dd \dl */
@@ -2620,7 +2695,7 @@ psql_completion(char *text, int start, int end)
        COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
    else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0
             || strncmp(prev_wd, "\\z", strlen("\\z")) == 0)
-       COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+       COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf, NULL);
    else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0)
        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
    else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0)
@@ -2635,7 +2710,7 @@ psql_completion(char *text, int start, int end)
 
    /* must be at end of \d list */
    else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0)
-       COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisv, NULL);
+       COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisvf, NULL);
 
    else if (strcmp(prev_wd, "\\ef") == 0)
        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
index 1c3d14951c0d38a109cec893c779b76fbae2849c..60d48cf18ff752716cb85a80bcb40043333520d6 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201012131
+#define CATALOG_VERSION_NO 201012151
 
 #endif
index 87f853d0e5e7ce54980274de43a0a0b7ccd41500..a2792606ab56f0f2e949664ba3aa6d5bb4ccb545 100644 (file)
@@ -137,6 +137,7 @@ typedef enum ObjectClass
    OCLASS_FDW,                 /* pg_foreign_data_wrapper */
    OCLASS_FOREIGN_SERVER,      /* pg_foreign_server */
    OCLASS_USER_MAPPING,        /* pg_user_mapping */
+   OCLASS_FOREIGN_TABLE,       /* pg_foreign_table */
    OCLASS_DEFACL,              /* pg_default_acl */
    MAX_OCLASS                  /* MUST BE LAST */
 } ObjectClass;
index 1542c8d5f4912b7ee1702f68e6aa5d613d273d4e..938add551fe5b6d2358cdf90d1ba05a88eb1d074 100644 (file)
@@ -275,6 +275,9 @@ DECLARE_UNIQUE_INDEX(pg_user_mapping_oid_index, 174, on pg_user_mapping using bt
 DECLARE_UNIQUE_INDEX(pg_user_mapping_user_server_index, 175, on pg_user_mapping using btree(umuser oid_ops, umserver oid_ops));
 #define UserMappingUserServerIndexId   175
 
+DECLARE_UNIQUE_INDEX(pg_foreign_table_relid_index, 3119, on pg_foreign_table using btree(ftrelid oid_ops));
+#define ForeignTableRelidIndexId 3119
+
 DECLARE_UNIQUE_INDEX(pg_default_acl_role_nsp_obj_index, 827, on pg_default_acl using btree(defaclrole oid_ops, defaclnamespace oid_ops, defaclobjtype char_ops));
 #define DefaultAclRoleNspObjIndexId 827
 DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btree(oid oid_ops));
index 1edbfe378b115f133dbf5b1987c95965e2e747c9..77a3ee35311e867d98d10063b792ca072a826c1d 100644 (file)
@@ -148,6 +148,7 @@ DESCR("");
 #define          RELKIND_TOASTVALUE      't'       /* moved off huge values */
 #define          RELKIND_VIEW            'v'       /* view */
 #define          RELKIND_COMPOSITE_TYPE  'c'       /* composite type */
+#define          RELKIND_FOREIGN_TABLE   'f'       /* foreign table */
 
 #define          RELPERSISTENCE_PERMANENT  'p'
 #define          RELPERSISTENCE_TEMP       't'
index 6dd01e60f672e84784efe49bab6efea88284ec12..b1fc2adf398a75527089567c092e54f9dfaf1ec9 100644 (file)
@@ -33,6 +33,7 @@ CATALOG(pg_foreign_data_wrapper,2328)
    NameData    fdwname;        /* foreign-data wrapper name */
    Oid         fdwowner;       /* FDW owner */
    Oid         fdwvalidator;   /* optional validation function */
+   Oid         fdwhandler;     /* foreign-data routines function */
 
    /* VARIABLE LENGTH FIELDS start here. */
 
@@ -52,11 +53,12 @@ typedef FormData_pg_foreign_data_wrapper *Form_pg_foreign_data_wrapper;
  * ----------------
  */
 
-#define Natts_pg_foreign_data_wrapper              5
+#define Natts_pg_foreign_data_wrapper              6
 #define Anum_pg_foreign_data_wrapper_fdwname       1
 #define Anum_pg_foreign_data_wrapper_fdwowner      2
 #define Anum_pg_foreign_data_wrapper_fdwvalidator  3
-#define Anum_pg_foreign_data_wrapper_fdwacl            4
-#define Anum_pg_foreign_data_wrapper_fdwoptions        5
+#define Anum_pg_foreign_data_wrapper_fdwhandler        4
+#define Anum_pg_foreign_data_wrapper_fdwacl            5
+#define Anum_pg_foreign_data_wrapper_fdwoptions        6
 
 #endif   /* PG_FOREIGN_DATA_WRAPPER_H */
diff --git a/src/include/catalog/pg_foreign_table.h b/src/include/catalog/pg_foreign_table.h
new file mode 100644 (file)
index 0000000..ada807a
--- /dev/null
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_foreign_table.h
+ *   definition of the system "foreign table" relation (pg_foreign_table)
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_foreign_table.h
+ *
+ * NOTES
+ *   the genbki.sh script reads this file and generates .bki
+ *   information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_FOREIGN_TABLE_H
+#define PG_FOREIGN_TABLE_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *     pg_foreign_table definition.  cpp turns this into
+ *     typedef struct FormData_pg_foreign_table
+ * ----------------
+ */
+#define ForeignTableRelationId 3118
+
+CATALOG(pg_foreign_table,3118) BKI_WITHOUT_OIDS
+{
+   Oid         ftrelid;        /* OID of foreign table */
+   Oid         ftserver;       /* OID of foreign server */
+   text        ftoptions[1];   /* FDW-specific options */
+} FormData_pg_foreign_table;
+
+/* ----------------
+ *     Form_pg_foreign_table corresponds to a pointer to a tuple with
+ *     the format of pg_foreign_table relation.
+ * ----------------
+ */
+typedef FormData_pg_foreign_table *Form_pg_foreign_table;
+
+/* ----------------
+ *     compiler constants for pg_foreign_table
+ * ----------------
+ */
+
+#define Natts_pg_foreign_table                 3
+#define Anum_pg_foreign_table_ftrelid          1
+#define Anum_pg_foreign_table_ftserver         2
+#define Anum_pg_foreign_table_ftoptions            3
+
+#endif   /* PG_FOREIGN_TABLE_H */
index feae22e896fc060aebf857e274bcb1339fa44be3..27f9d28580503bb9f1cfe99e63ae8fcc74950d7a 100644 (file)
@@ -3871,6 +3871,10 @@ DATA(insert OID = 2777 (  anynonarray_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 27
 DESCR("I/O");
 DATA(insert OID = 2778 (  anynonarray_out  PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2275 "2776" _null_ _null_ _null_ _null_ anynonarray_out _null_ _null_ _null_ ));
 DESCR("I/O");
+DATA(insert OID = 3116 (  fdw_handler_in   PGNSP PGUID 12 1 0 0 f f f f f i 1 0 3115 "2275" _null_ _null_ _null_ _null_ fdw_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3117 (  fdw_handler_out  PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2275 "3115" _null_ _null_ _null_ _null_ fdw_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
 
 /* cryptographic */
 DATA(insert OID =  2311 (  md5    PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
index 201e5dbc1f59c05128ff647084c9645d699edfe1..f0241d4bcb5cc36e9d68eebfe5e5e60ce9d2fec7 100644 (file)
@@ -623,6 +623,8 @@ DATA(insert OID = 2776 ( anynonarray    PGNSP PGUID  4 t p P f t \054 0 0 0 anynona
 #define ANYNONARRAYOID 2776
 DATA(insert OID = 3500 ( anyenum       PGNSP PGUID  4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYENUMOID     3500
+DATA(insert OID = 3115 ( fdw_handler   PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+#define FDW_HANDLEROID     3115
 
 
 /*
index 86d62af9ed2b9e0c6098bb6842ce9426ac9b5875..491caf78fc76fd0aadc423d82c210ac3fa031fa5 100644 (file)
@@ -154,6 +154,7 @@ extern void CreateUserMapping(CreateUserMappingStmt *stmt);
 extern void AlterUserMapping(AlterUserMappingStmt *stmt);
 extern void RemoveUserMapping(DropUserMappingStmt *stmt);
 extern void RemoveUserMappingById(Oid umId);
+extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
 
 /* support routines in commands/define.c */
 
index 2305929b3594f7db93bbfe79b202b046e95e8cb8..2326fad110dc238a91b20f817e198e232da11079 100644 (file)
@@ -13,7 +13,8 @@
 #ifndef FOREIGN_H
 #define FOREIGN_H
 
-#include "nodes/parsenodes.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
 
 
 /* Helper for obtaining username for user mapping */
@@ -29,7 +30,8 @@ typedef enum
 {
    ServerOpt = 1,              /* options applicable to SERVER */
    UserMappingOpt = 2,         /* options for USER MAPPING */
-   FdwOpt = 4                  /* options for FOREIGN DATA WRAPPER */
+   FdwOpt = 4,                 /* options for FOREIGN DATA WRAPPER */
+   ForeignTableOpt = 8,        /* options for FOREIGN TABLE */
 } GenericOptionFlags;
 
 typedef struct ForeignDataWrapper
@@ -38,6 +40,7 @@ typedef struct ForeignDataWrapper
    Oid         owner;          /* FDW owner user Oid */
    char       *fdwname;        /* Name of the FDW */
    Oid         fdwvalidator;
+   Oid         fdwhandler;
    List       *options;        /* fdwoptions as DefElem list */
 } ForeignDataWrapper;
 
@@ -59,7 +62,14 @@ typedef struct UserMapping
    List       *options;        /* useoptions as DefElem list */
 } UserMapping;
 
+typedef struct ForeignTable
+{
+   Oid         relid;          /* relation Oid */
+   Oid         serverid;       /* server Oid */
+   List       *options;        /* ftoptions as DefElem list */
+} ForeignTable;
 
+/* catalog manipulation */
 extern ForeignServer *GetForeignServer(Oid serverid);
 extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok);
 extern Oid GetForeignServerOidByName(const char *name, bool missing_ok);
@@ -68,5 +78,15 @@ extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
 extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
                            bool missing_ok);
 extern Oid GetForeignDataWrapperOidByName(const char *name, bool missing_ok);
+extern ForeignTable *GetForeignTable(Oid relid);
+extern bool IsForeignTable(Oid relid);
+extern Oid GetFdwValidator(Oid relid);
+extern List *GetGenericOptionsPerColumn(Oid relid, int2 attnum);
+
+extern int flatten_generic_options(List *options,
+                          const char **keywords, const char **values);
+
+/* ALTER FOREIGN TABLE ... OPTIONS (...) handlers */
+extern void ATExecGenericOptions(Relation rel, List *options);
 
 #endif   /* FOREIGN_H */
index bc96ebf68e4d066d6901cb64d4f02e615e16a6c6..1bb5a11994dcf9fe0f063382daa475ece7a12e5e 100644 (file)
@@ -353,6 +353,7 @@ typedef enum NodeTag
    T_DropUserMappingStmt,
    T_AlterTableSpaceOptionsStmt,
    T_SecLabelStmt,
+   T_CreateForeignTableStmt,
 
    /*
     * TAGS FOR PARSE TREE NODES (parsenodes.h)
index 8b34b7630093ad23286e99d2d4db4ce221aa1235..5b5e621618f6d8e984383a73a41e8da63818c8a5 100644 (file)
@@ -501,7 +501,10 @@ typedef enum CreateStmtLikeOption
    CREATE_TABLE_LIKE_INDEXES = 1 << 2,
    CREATE_TABLE_LIKE_STORAGE = 1 << 3,
    CREATE_TABLE_LIKE_COMMENTS = 1 << 4,
-   CREATE_TABLE_LIKE_ALL = 0x7FFFFFFF
+   CREATE_TABLE_LIKE_ALL = 0x7FFFFFFF,
+   /* FOREIGN TABLE supports only CONSTRAINT/COMMENT. */
+   CREATE_TABLE_LIKE_FOREIGN_ALL = ( CREATE_TABLE_LIKE_CONSTRAINTS |
+                                     CREATE_TABLE_LIKE_COMMENTS)
 } CreateStmtLikeOption;
 
 /*
@@ -1066,6 +1069,7 @@ typedef enum ObjectType
    OBJECT_DOMAIN,
    OBJECT_FDW,
    OBJECT_FOREIGN_SERVER,
+   OBJECT_FOREIGN_TABLE,
    OBJECT_FUNCTION,
    OBJECT_INDEX,
    OBJECT_LANGUAGE,
@@ -1165,7 +1169,8 @@ typedef enum AlterTableType
    AT_EnableReplicaRule,       /* ENABLE REPLICA RULE name */
    AT_DisableRule,             /* DISABLE RULE name */
    AT_AddInherit,              /* INHERIT parent */
-   AT_DropInherit              /* NO INHERIT parent */
+   AT_DropInherit,             /* NO INHERIT parent */
+   AT_GenericOptions,          /* OPTIONS (...) */
 } AlterTableType;
 
 typedef struct AlterTableCmd   /* one subcommand of an ALTER TABLE */
@@ -1226,6 +1231,7 @@ typedef enum GrantObjectType
    ACL_OBJECT_DATABASE,        /* database */
    ACL_OBJECT_FDW,             /* foreign-data wrapper */
    ACL_OBJECT_FOREIGN_SERVER,  /* foreign server */
+   ACL_OBJECT_FOREIGN_TABLE,   /* foreign table */
    ACL_OBJECT_FUNCTION,        /* function */
    ACL_OBJECT_LANGUAGE,        /* procedural language */
    ACL_OBJECT_LARGEOBJECT,     /* largeobject */
@@ -1526,7 +1532,7 @@ typedef struct CreateFdwStmt
 {
    NodeTag     type;
    char       *fdwname;        /* foreign-data wrapper name */
-   List       *validator;      /* optional validator function (qual. name) */
+   List       *func_options;   /* VALIDATOR/HANDLER conbination */
    List       *options;        /* generic options to FDW */
 } CreateFdwStmt;
 
@@ -1534,8 +1540,7 @@ typedef struct AlterFdwStmt
 {
    NodeTag     type;
    char       *fdwname;        /* foreign-data wrapper name */
-   List       *validator;      /* optional validator function (qual. name) */
-   bool        change_validator;
+   List       *func_options;   /* VALIDATOR/HANDLER conbination */
    List       *options;        /* generic options to FDW */
 } AlterFdwStmt;
 
@@ -1579,6 +1584,18 @@ typedef struct DropForeignServerStmt
    DropBehavior behavior;      /* drop behavior - cascade/restrict */
 } DropForeignServerStmt;
 
+/* ----------------------
+ *     Create FOREIGN TABLE Statements
+ * ----------------------
+ */
+
+typedef struct CreateForeignTableStmt
+{
+   CreateStmt  base;
+   char       *servername;
+   List       *options;
+} CreateForeignTableStmt;
+
 /* ----------------------
  *     Create/Drop USER MAPPING Statements
  * ----------------------
index 430dc1f61e14902c2f4f0d7c06e751030f4b4623..b35f6baebafaad8a0ed3191379974ae94e8cf123 100644 (file)
@@ -150,6 +150,7 @@ typedef ArrayType Acl;
 #define ACL_ALL_RIGHTS_DATABASE        (ACL_CREATE|ACL_CREATE_TEMP|ACL_CONNECT)
 #define ACL_ALL_RIGHTS_FDW         (ACL_USAGE)
 #define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE)
+#define ACL_ALL_RIGHTS_FOREIGN_TABLE (ACL_SELECT)
 #define ACL_ALL_RIGHTS_FUNCTION        (ACL_EXECUTE)
 #define ACL_ALL_RIGHTS_LANGUAGE        (ACL_USAGE)
 #define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE)
@@ -193,6 +194,7 @@ typedef enum AclObjectKind
    ACL_KIND_TSCONFIGURATION,   /* pg_ts_config */
    ACL_KIND_FDW,               /* pg_foreign_data_wrapper */
    ACL_KIND_FOREIGN_SERVER,    /* pg_foreign_server */
+   ACL_KIND_FOREIGN_TABLE,     /* pg_foreign_table */
    MAX_ACL_KIND                /* MUST BE LAST */
 } AclObjectKind;
 
index a2fb7494cb4cb28ec9fc21fc34762ec989778151..f3f242f6f9af354fae0d8db0750a90d93e2a61dc 100644 (file)
@@ -515,6 +515,8 @@ extern Datum pg_node_tree_in(PG_FUNCTION_ARGS);
 extern Datum pg_node_tree_out(PG_FUNCTION_ARGS);
 extern Datum pg_node_tree_recv(PG_FUNCTION_ARGS);
 extern Datum pg_node_tree_send(PG_FUNCTION_ARGS);
+extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
+extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
 
 /* regexp.c */
 extern Datum nameregexeq(PG_FUNCTION_ARGS);
index 5cab47a092bd73cbf1cc74315f85ae581db95b1b..e6c422b7aad12baf0aaac76ee45730f18fd79f22 100644 (file)
 #define ERRCODE_CONFIG_FILE_ERROR          MAKE_SQLSTATE('F','0', '0','0','0')
 #define ERRCODE_LOCK_FILE_EXISTS           MAKE_SQLSTATE('F','0', '0','0','1')
 
+/* Class HV - Foreign data wrapper Error (SQL/MED-specific error class) */
+#define ERRCODE_FDW_ERROR                  MAKE_SQLSTATE('H','V', '0','0','0')
+#define ERRCODE_FDW_COLUMN_NAME_NOT_FOUND  MAKE_SQLSTATE('H','V', '0','0','5')
+#define ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED MAKE_SQLSTATE('H','V', '0','0','2')
+#define ERRCODE_FDW_FUNCTION_SEQUENCE_ERROR    MAKE_SQLSTATE('H','V', '0','1','0')
+#define ERRCODE_FDW_INCONSISTENT_DESCRIPTOR_INFORMATION    MAKE_SQLSTATE('H','V', '0','2','1')
+#define ERRCODE_FDW_INVALID_ATTRIBUTE_VALUE    MAKE_SQLSTATE('H','V', '0','2','4')
+#define ERRCODE_FDW_INVALID_COLUMN_NAME    MAKE_SQLSTATE('H','V', '0','0','7')
+#define ERRCODE_FDW_INVALID_COLUMN_NUMBER  MAKE_SQLSTATE('H','V', '0','0','8')
+#define ERRCODE_FDW_INVALID_DATA_TYPE      MAKE_SQLSTATE('H','V', '0','0','4')
+#define ERRCODE_FDW_INVALID_DATA_TYPE_DESCRIPTORS  MAKE_SQLSTATE('H','V', '0','0','6')
+#define ERRCODE_FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER    MAKE_SQLSTATE('H','V', '0','9','1')
+#define ERRCODE_FDW_INVALID_HANDLE         MAKE_SQLSTATE('H','V', '0','0','B')
+#define ERRCODE_FDW_INVALID_OPTION_INDEX   MAKE_SQLSTATE('H','V', '0','0','C')
+#define ERRCODE_FDW_INVALID_OPTION_NAME        MAKE_SQLSTATE('H','V', '0','0','D')
+#define ERRCODE_FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH MAKE_SQLSTATE('H','V', '0','9','0')
+#define ERRCODE_FDW_INVALID_STRING_FORMAT  MAKE_SQLSTATE('H','V', '0','0','A')
+#define ERRCODE_FDW_INVALID_USE_OF_NULL_POINTER    MAKE_SQLSTATE('H','V', '0','0','9')
+#define ERRCODE_FDW_TOO_MANY_HANDLES       MAKE_SQLSTATE('H','V', '0','1','4')
+#define ERRCODE_FDW_OUT_OF_MEMORY          MAKE_SQLSTATE('H','V', '0','0','1')
+#define ERRCODE_FDW_NO_SCHEMAS             MAKE_SQLSTATE('H','V', '0','0','P')
+#define ERRCODE_FDW_OPTION_NAME_NOT_FOUND  MAKE_SQLSTATE('H','V', '0','0','J')
+#define ERRCODE_FDW_REPLY_HANDLE           MAKE_SQLSTATE('H','V', '0','0','K')
+#define ERRCODE_FDW_SCHEMA_NOT_FOUND       MAKE_SQLSTATE('H','V', '0','0','Q')
+#define ERRCODE_FDW_TABLE_NOT_FOUND            MAKE_SQLSTATE('H','V', '0','0','R')
+#define ERRCODE_FDW_UNALBE_TO_CREATE_EXECUTION MAKE_SQLSTATE('H','V', '0','0','L')
+#define ERRCODE_FDW_UNABLE_TO_CREATE_REPLY MAKE_SQLSTATE('H','V', '0','0','M')
+#define ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION MAKE_SQLSTATE('H','V', '0','0','N')
+
 /* Class P0 - PL/pgSQL Error (PostgreSQL-specific error class) */
 #define ERRCODE_PLPGSQL_ERROR              MAKE_SQLSTATE('P','0', '0','0','0')
 #define ERRCODE_RAISE_EXCEPTION                MAKE_SQLSTATE('P','0', '0','0','1')
index 30e0f8f3bd7ccb051a5b7534b936065b328d3acb..2204d0e4b11b0d172fd6acfb4890d835c105b8b5 100644 (file)
@@ -56,6 +56,7 @@ enum SysCacheIdentifier
    FOREIGNDATAWRAPPEROID,
    FOREIGNSERVERNAME,
    FOREIGNSERVEROID,
+   FOREIGNTABLEREL,
    INDEXRELID,
    LANGNAME,
    LANGOID,
index e415730bd09fb67c57963ba819353c17efec67a3..f25682c74e4950cb813682db1a205f1dd06668a5 100644 (file)
@@ -599,9 +599,9 @@ ERROR:  cannot alter system column "oid"
 -- try creating a view and altering that, should fail
 create view myview as select * from atacc1;
 alter table myview alter column test drop not null;
-ERROR:  "myview" is not a table
+ERROR:  "myview" is not a table or foreign table
 alter table myview alter column test set not null;
-ERROR:  "myview" is not a table
+ERROR:  "myview" is not a table or foreign table
 drop view myview;
 drop table atacc1;
 -- test inheritance
@@ -854,7 +854,7 @@ select * from myview;
 (0 rows)
 
 alter table myview drop d;
-ERROR:  "myview" is not a table or composite type
+ERROR:  "myview" is not a table or composite type or foreign table
 drop view myview;
 -- test some commands to make sure they fail on the dropped column
 analyze atacc1(a);
index fcc1d7cceacf8a7904babfee3f3f1b53a4eb335d..8bc55ea23c547cebe51321dda45e5c64830b630e 100644 (file)
@@ -13,14 +13,15 @@ CREATE ROLE regress_test_role2;
 CREATE ROLE regress_test_role_super SUPERUSER;
 CREATE ROLE regress_test_indirect;
 CREATE ROLE unprivileged_role;
+CREATE SCHEMA foreign_schema;
 CREATE FOREIGN DATA WRAPPER dummy;
 CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
 -- At this point we should have 2 built-in wrappers and no servers.
-SELECT fdwname, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
-  fdwname   |       fdwvalidator       | fdwoptions 
-------------+--------------------------+------------
- dummy      | -                        | 
- postgresql | postgresql_fdw_validator | 
+SELECT fdwname, fdwvalidator::regproc, fdwhandler::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY fdwname;
+  fdwname   |       fdwvalidator       | fdwhandler | fdwoptions 
+------------+--------------------------+------------+------------
+ dummy      | -                        | -          | 
+ postgresql | postgresql_fdw_validator | -          | 
 (2 rows)
 
 SELECT srvname, srvoptions FROM pg_foreign_server;
@@ -38,12 +39,12 @@ CREATE FOREIGN DATA WRAPPER foo VALIDATOR bar;            -- ERROR
 ERROR:  function bar(text[], oid) does not exist
 CREATE FOREIGN DATA WRAPPER foo;
 \dew
-               List of foreign-data wrappers
-    Name    |       Owner       |        Validator         
-------------+-------------------+--------------------------
- dummy      | foreign_data_user | -
- foo        | foreign_data_user | -
- postgresql | foreign_data_user | postgresql_fdw_validator
+                    List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         
+------------+-------------------+---------+--------------------------
+ dummy      | foreign_data_user | -       | -
+ foo        | foreign_data_user | -       | -
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator
 (3 rows)
 
 CREATE FOREIGN DATA WRAPPER foo; -- duplicate
@@ -51,12 +52,12 @@ ERROR:  foreign-data wrapper "foo" already exists
 DROP FOREIGN DATA WRAPPER foo;
 CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
 \dew+
-                                List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |   Options   
-------------+-------------------+--------------------------+-------------------+-------------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {testing=1}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                     List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |   Options   
+------------+-------------------+---------+--------------------------+-------------------+-------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {testing=1}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 DROP FOREIGN DATA WRAPPER foo;
@@ -64,12 +65,12 @@ CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', testing '2');   -- ERROR
 ERROR:  option "testing" provided more than once
 CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
 \dew+
-                                     List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |        Options        
-------------+-------------------+--------------------------+-------------------+-----------------------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {testing=1,another=2}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                          List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |        Options        
+------------+-------------------+---------+--------------------------+-------------------+-----------------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {testing=1,another=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 DROP FOREIGN DATA WRAPPER foo;
@@ -80,12 +81,12 @@ HINT:  Must be superuser to create a foreign-data wrapper.
 RESET ROLE;
 CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | postgresql_fdw_validator |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 -- ALTER FOREIGN DATA WRAPPER
@@ -97,12 +98,12 @@ ALTER FOREIGN DATA WRAPPER foo VALIDATOR bar;               -- ERROR
 ERROR:  function bar(text[], oid) does not exist
 ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
@@ -112,34 +113,34 @@ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP c);            -- ERROR
 ERROR:  option "c" not found
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
 \dew+
-                               List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |  Options  
-------------+-------------------+--------------------------+-------------------+-----------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {a=1,b=2}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                    List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
+------------+-------------------+---------+--------------------------+-------------------+-----------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {a=1,b=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
 \dew+
-                               List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |  Options  
-------------+-------------------+--------------------------+-------------------+-----------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {b=3,c=4}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                    List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
+------------+-------------------+---------+--------------------------+-------------------+-----------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
 ERROR:  option "b" provided more than once
 \dew+
-                                 List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |    Options    
-------------+-------------------+--------------------------+-------------------+---------------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {b=3,c=4,a=2}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                      List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |    Options    
+------------+-------------------+---------+--------------------------+-------------------+---------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 SET ROLE regress_test_role;
@@ -149,12 +150,12 @@ HINT:  Must be superuser to alter a foreign-data wrapper.
 SET ROLE regress_test_role_super;
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
 \dew+
-                                   List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |      Options      
-------------+-------------------+--------------------------+-------------------+-------------------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                        List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |      Options      
+------------+-------------------+---------+--------------------------+-------------------+-------------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
@@ -168,12 +169,12 @@ ERROR:  permission denied to alter foreign-data wrapper "foo"
 HINT:  Must be superuser to alter a foreign-data wrapper.
 RESET ROLE;
 \dew+
-                                      List of foreign-data wrappers
-    Name    |          Owner          |        Validator         | Access privileges |      Options      
-------------+-------------------------+--------------------------+-------------------+-------------------
- dummy      | foreign_data_user       | -                        |                   | 
- foo        | regress_test_role_super | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user       | postgresql_fdw_validator |                   | 
+                                           List of foreign-data wrappers
+    Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
+------------+-------------------------+---------+--------------------------+-------------------+-------------------
+ dummy      | foreign_data_user       | -       | -                        |                   | 
+ foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 -- DROP FOREIGN DATA WRAPPER
@@ -182,12 +183,12 @@ ERROR:  foreign-data wrapper "nonexistent" does not exist
 DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
 NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
 \dew+
-                                      List of foreign-data wrappers
-    Name    |          Owner          |        Validator         | Access privileges |      Options      
-------------+-------------------------+--------------------------+-------------------+-------------------
- dummy      | foreign_data_user       | -                        |                   | 
- foo        | regress_test_role_super | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user       | postgresql_fdw_validator |                   | 
+                                           List of foreign-data wrappers
+    Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
+------------+-------------------------+---------+--------------------------+-------------------+-------------------
+ dummy      | foreign_data_user       | -       | -                        |                   | 
+ foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 DROP ROLE regress_test_role_super;                          -- ERROR
@@ -202,23 +203,23 @@ ALTER ROLE regress_test_role_super SUPERUSER;
 DROP FOREIGN DATA WRAPPER foo;
 DROP ROLE regress_test_role_super;
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (2 rows)
 
 CREATE FOREIGN DATA WRAPPER foo;
 CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
 CREATE USER MAPPING FOR current_user SERVER s1;
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 \des+
@@ -250,11 +251,11 @@ NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to server s1
 drop cascades to user mapping for foreign_data_user
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (2 rows)
 
 \des+
@@ -350,6 +351,7 @@ CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
 
 RESET ROLE;
 REVOKE regress_test_indirect FROM regress_test_role;
+CREATE SERVER sc FOREIGN DATA WRAPPER dummy;
 -- ALTER SERVER
 ALTER SERVER s0;                                            -- ERROR
 ERROR:  syntax error at or near ";"
@@ -376,9 +378,10 @@ GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
       |                   |                      | regress_test_role2=U*/foreign_data_user |        |         | 
  s7   | foreign_data_user | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
  s8   | foreign_data_user | postgresql           |                                         |        |         | {host=localhost,dbname=s8db}
+ sc   | foreign_data_user | dummy                |                                         |        |         | 
  t1   | regress_test_role | foo                  |                                         |        |         | 
  t2   | regress_test_role | foo                  |                                         |        |         | 
-(10 rows)
+(11 rows)
 
 SET ROLE regress_test_role;
 ALTER SERVER s1 VERSION '1.1';                              -- ERROR
@@ -427,9 +430,10 @@ privileges for foreign-data wrapper foo
       |                       |                      | regress_test_role2=U*/foreign_data_user |        |         | 
  s7   | foreign_data_user     | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
  s8   | foreign_data_user     | postgresql           |                                         |        |         | {dbname=db1,connect_timeout=30}
+ sc   | foreign_data_user     | dummy                |                                         |        |         | 
  t1   | regress_test_role     | foo                  |                                         |        |         | 
  t2   | regress_test_role     | foo                  |                                         |        |         | 
-(10 rows)
+(11 rows)
 
 -- DROP SERVER
 DROP SERVER nonexistent;                                    -- ERROR
@@ -448,9 +452,10 @@ NOTICE:  server "nonexistent" does not exist, skipping
  s6   | foreign_data_user     | foo
  s7   | foreign_data_user     | foo
  s8   | foreign_data_user     | postgresql
+ sc   | foreign_data_user     | dummy
  t1   | regress_test_role     | foo
  t2   | regress_test_role     | foo
-(10 rows)
+(11 rows)
 
 SET ROLE regress_test_role;
 DROP SERVER s2;                                             -- ERROR
@@ -468,9 +473,10 @@ RESET ROLE;
  s6   | foreign_data_user | foo
  s7   | foreign_data_user | foo
  s8   | foreign_data_user | postgresql
+ sc   | foreign_data_user | dummy
  t1   | regress_test_role | foo
  t2   | regress_test_role | foo
-(9 rows)
+(10 rows)
 
 ALTER SERVER s2 OWNER TO regress_test_role;
 SET ROLE regress_test_role;
@@ -486,9 +492,10 @@ RESET ROLE;
  s6   | foreign_data_user | foo
  s7   | foreign_data_user | foo
  s8   | foreign_data_user | postgresql
+ sc   | foreign_data_user | dummy
  t1   | regress_test_role | foo
  t2   | regress_test_role | foo
-(8 rows)
+(9 rows)
 
 CREATE USER MAPPING FOR current_user SERVER s3;
 \deu
@@ -513,9 +520,10 @@ NOTICE:  drop cascades to user mapping for foreign_data_user
  s6   | foreign_data_user | foo
  s7   | foreign_data_user | foo
  s8   | foreign_data_user | postgresql
+ sc   | foreign_data_user | dummy
  t1   | regress_test_role | foo
  t2   | regress_test_role | foo
-(7 rows)
+(8 rows)
 
 \deu
 List of user mappings
@@ -546,6 +554,7 @@ ERROR:  permission denied for foreign server s7
 CREATE USER MAPPING FOR public SERVER s8;                   -- ERROR
 ERROR:  must be owner of foreign server s8
 RESET ROLE;
+CREATE USER MAPPING FOR public SERVER sc;
 ALTER SERVER t1 OWNER TO regress_test_indirect;
 SET ROLE regress_test_role;
 CREATE USER MAPPING FOR current_user SERVER t1 OPTIONS (username 'bob', password 'boo');
@@ -560,9 +569,10 @@ RESET ROLE;
  s5     | regress_test_role
  s6     | regress_test_role
  s8     | foreign_data_user
+ sc     | public
  t1     | public
  t1     | regress_test_role
-(7 rows)
+(8 rows)
 
 -- ALTER USER MAPPING
 ALTER USER MAPPING FOR regress_test_missing_role SERVER s4 OPTIONS (gotcha 'true'); -- ERROR
@@ -590,9 +600,10 @@ RESET ROLE;
  s5     | regress_test_role | {modified=1}
  s6     | regress_test_role | {username=test}
  s8     | foreign_data_user | {password=public}
+ sc     | public            | 
  t1     | public            | {modified=1}
  t1     | regress_test_role | {username=bob,password=boo}
-(7 rows)
+(8 rows)
 
 -- DROP USER MAPPING
 DROP USER MAPPING FOR regress_test_missing_role SERVER s4;  -- ERROR
@@ -623,10 +634,162 @@ DROP SERVER s7;
  s6     | regress_test_role
  s8     | foreign_data_user
  s8     | public
+ sc     | public
  t1     | public
  t1     | regress_test_role
-(8 rows)
+(9 rows)
 
+-- CREATE FOREIGN TABLE
+CREATE TABLE t1 (
+   c1 integer NOT NULL,
+   c2 text,
+   c3 date CHECK (c3 > '2000-01-01'::date),
+   CONSTRAINT t1_c1_check CHECK (c1 > 0)
+);
+CREATE TABLE t2 ();
+CREATE TABLE f1
+CREATE FOREIGN TABLE ft1 ();                                    -- ERROR
+ERROR:  syntax error at or near "CREATE"
+LINE 2: CREATE FOREIGN TABLE ft1 ();
+        ^
+CREATE FOREIGN TABLE ft1 () SERVER no_server;                   -- ERROR
+ERROR:  server "no_server" does not exist
+CREATE FOREIGN TABLE ft1 () INHERITS () SERVER sc;              -- ERROR
+ERROR:  syntax error at or near ")"
+LINE 1: CREATE FOREIGN TABLE ft1 () INHERITS () SERVER sc;
+                                              ^
+CREATE FOREIGN TABLE ft1 () INHERITS (no_table) SERVER sc;      -- ERROR
+ERROR:  relation "no_table" does not exist
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER sc;                 -- ERROR
+NOTICE:  CREATE TABLE will create implicit sequence "ft1_c1_seq" for serial column "ft1.c1"
+ERROR:  referenced relation "ft1" is not a table
+CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;                -- ERROR
+ERROR:  syntax error at or near "WITH OIDS"
+LINE 1: CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;
+                                              ^
+CREATE FOREIGN TABLE ft1 (
+   c1 integer NOT NULL,
+   c2 text,
+   c3 date CHECK (c3 > '2000-01-01'::date),
+   CONSTRAINT ft1_c1_check CHECK (c1 > 0)
+) SERVER sc OPTIONS (delimiter ',', quote '"');
+\d+ ft1
+              Foreign table "public.ft1"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ c1     | integer | not null  | plain    | 
+ c2     | text    |           | extended | 
+ c3     | date    |           | plain    | 
+Check constraints:
+    "ft1_c1_check" CHECK (c1 > 0)
+    "ft1_c3_check" CHECK (c3 > '01-01-2000'::date)
+Server: sc
+Has OIDs: no
+
+CREATE FOREIGN TABLE ft2 () INHERITS (t1) SERVER sc OPTIONS (delimiter ' ', quote '`');
+\d+ ft2
+              Foreign table "public.ft2"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ c1     | integer | not null  | plain    | 
+ c2     | text    |           | extended | 
+ c3     | date    |           | plain    | 
+Check constraints:
+    "t1_c1_check" CHECK (c1 > 0)
+    "t1_c3_check" CHECK (c3 > '01-01-2000'::date)
+Server: sc
+Inherits: t1
+Has OIDs: no
+
+\det+
+                List of foreign tables
+ Schema | Table | Server |          Options           
+--------+-------+--------+----------------------------
+ public | ft1   | sc     | {"delimiter=,","quote=\""}
+ public | ft2   | sc     | {"delimiter= ",quote=`}
+(2 rows)
+
+CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
+ERROR:  "ft1" is not a table
+-- ALTER FOREIGN TABLE
+COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
+COMMENT ON FOREIGN TABLE ft1 IS NULL;
+COMMENT ON COLUMN ft1.c1 IS 'foreign column';
+COMMENT ON COLUMN ft1.c1 IS NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0;        -- ERROR
+ERROR:  syntax error at or near "DEFAULT"
+LINE 1: ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0;
+                                                      ^
+ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0;          -- ERROR
+ERROR:  syntax error at or near "DEFAULT"
+LINE 1: ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0;
+                                                    ^
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT;           -- ERROR
+ERROR:  syntax error at or near "DEFAULT"
+LINE 1: ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT;
+                                                     ^
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
+ERROR:  syntax error at or near "using"
+LINE 1: ...R FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0';
+                                                             ^
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const;               -- ERROR
+ERROR:  constraint "no_const" of relation "ft1" does not exist
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
+NOTICE:  constraint "no_const" of relation "ft1" does not exist, skipping
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ALTER FOREIGN TABLE ft1 SET WITH OIDS;                          -- ERROR
+ERROR:  syntax error at or near "WITH OIDS"
+LINE 1: ALTER FOREIGN TABLE ft1 SET WITH OIDS;
+                                    ^
+ALTER FOREIGN TABLE ft1 INHERIT t2;
+ALTER FOREIGN TABLE ft1 NO INHERIT t2;
+ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
+ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@');
+ALTER FOREIGN TABLE ft1 DROP COLUMN no_column;                  -- ERROR
+ERROR:  column "no_column" of relation "ft1" does not exist
+ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column;
+NOTICE:  column "no_column" of relation "ft1" does not exist, skipping
+ALTER FOREIGN TABLE ft1 DROP COLUMN c9;
+ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema;
+ALTER FOREIGN TABLE ft1 SET TABLESPACE ts;                      -- ERROR
+ERROR:  syntax error at or near "TABLESPACE"
+LINE 1: ALTER FOREIGN TABLE ft1 SET TABLESPACE ts;
+                                    ^
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
+\d foreign_schema.foreign_table_1
+Foreign table "foreign_schema.foreign_table_1"
+      Column      |  Type   | Modifiers 
+------------------+---------+-----------
+ foreign_column_1 | integer | not null
+ c2               | text    | 
+ c3               | date    | 
+ c4               | integer | 
+ c6               | integer | not null
+ c7               | integer | 
+ c8               | text    | 
+ c10              | integer | 
+Check constraints:
+    "ft1_c3_check" CHECK (c3 > '01-01-2000'::date)
+Server: sc
+
+-- DROP FOREIGN TABLE
+DROP FOREIGN TABLE no_table;                                    -- ERROR
+ERROR:  foreign table "no_table" does not exist
+DROP FOREIGN TABLE IF EXISTS no_table;
+NOTICE:  foreign table "no_table" does not exist, skipping
+DROP FOREIGN TABLE foreign_schema.foreign_table_1;
 -- Information schema
 SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
  foreign_data_wrapper_catalog | foreign_data_wrapper_name | authorization_identifier | library_name | foreign_data_wrapper_language 
@@ -649,9 +812,10 @@ SELECT * FROM information_schema.foreign_servers ORDER BY 1, 2;
  regression             | s5                  | regression                   | foo                       |                     | 15.0                   | regress_test_role
  regression             | s6                  | regression                   | foo                       |                     | 16.0                   | regress_test_indirect
  regression             | s8                  | regression                   | postgresql                |                     |                        | foreign_data_user
+ regression             | sc                  | regression                   | dummy                     |                     |                        | foreign_data_user
  regression             | t1                  | regression                   | foo                       |                     |                        | regress_test_indirect
  regression             | t2                  | regression                   | foo                       |                     |                        | regress_test_role
-(6 rows)
+(7 rows)
 
 SELECT * FROM information_schema.foreign_server_options ORDER BY 1, 2, 3;
  foreign_server_catalog | foreign_server_name |   option_name   | option_value 
@@ -671,11 +835,12 @@ SELECT * FROM information_schema.user_mappings ORDER BY lower(authorization_iden
  foreign_data_user        | regression             | s8
  PUBLIC                   | regression             | s4
  PUBLIC                   | regression             | s8
+ PUBLIC                   | regression             | sc
  PUBLIC                   | regression             | t1
  regress_test_role        | regression             | s5
  regress_test_role        | regression             | s6
  regress_test_role        | regression             | t1
-(8 rows)
+(9 rows)
 
 SELECT * FROM information_schema.user_mapping_options ORDER BY lower(authorization_identifier), 2, 3, 4;
  authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value 
@@ -707,6 +872,19 @@ SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREI
  foreign_data_user | regress_test_role2    | regression     |               | s6          | FOREIGN SERVER       | USAGE          | YES
 (4 rows)
 
+SELECT * FROM information_schema.foreign_tables ORDER BY 1, 2, 3;
+ foreign_table_catalog | foreign_table_schema | foreign_table_name | foreign_server_catalog | foreign_server_name 
+-----------------------+----------------------+--------------------+------------------------+---------------------
+ regression            | public               | ft2                | regression             | sc
+(1 row)
+
+SELECT * FROM information_schema.foreign_table_options ORDER BY 1, 2, 3, 4;
+ foreign_table_catalog | foreign_table_schema | foreign_table_name | option_name | option_value 
+-----------------------+----------------------+--------------------+-------------+--------------
+ regression            | public               | ft2                | delimiter   |  
+ regression            | public               | ft2                | quote       | `
+(2 rows)
+
 SET ROLE regress_test_role;
 SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4;
  authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value 
@@ -939,6 +1117,10 @@ DROP SERVER s9 CASCADE;                                         -- ERROR
 ERROR:  must be owner of foreign server s9
 RESET ROLE;
 -- Cleanup
+DROP SCHEMA foreign_schema CASCADE;
+DROP TABLE t1 CASCADE;
+NOTICE:  drop cascades to foreign table ft2
+DROP TABLE t2 CASCADE;
 DROP ROLE regress_test_role;                                -- ERROR
 ERROR:  role "regress_test_role" cannot be dropped because some objects depend on it
 DETAIL:  privileges for server s4
@@ -974,12 +1156,15 @@ DROP ROLE unprivileged_role;
 DROP ROLE regress_test_role2;
 DROP FOREIGN DATA WRAPPER postgresql CASCADE;
 DROP FOREIGN DATA WRAPPER dummy CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to server sc
+drop cascades to user mapping for public
 \c
 DROP ROLE foreign_data_user;
 -- At this point we should have no wrappers, no servers, and no mappings.
-SELECT fdwname, fdwvalidator, fdwoptions FROM pg_foreign_data_wrapper;
- fdwname | fdwvalidator | fdwoptions 
----------+--------------+------------
+SELECT fdwname, fdwvalidator, fdwhandler, fdwoptions FROM pg_foreign_data_wrapper;
+ fdwname | fdwvalidator | fdwhandler | fdwoptions 
+---------+--------------+------------+------------
 (0 rows)
 
 SELECT srvname, srvoptions FROM pg_foreign_server;
index 9596b0b712b71e1eaba89a148eea9c8f1f33c6a6..1ee820fd7c9400103adc436ca5c36304a6cc181d 100644 (file)
@@ -102,6 +102,7 @@ SELECT relname, relhasindex
  pg_enum                 | t
  pg_foreign_data_wrapper | t
  pg_foreign_server       | t
+ pg_foreign_table        | t
  pg_index                | t
  pg_inherits             | t
  pg_language             | t
@@ -154,7 +155,7 @@ SELECT relname, relhasindex
  timetz_tbl              | f
  tinterval_tbl           | f
  varchar_tbl             | f
-(143 rows)
+(144 rows)
 
 --
 -- another sanity check: every system catalog that has OIDs should have
index 556672d408a9ca2b27747fe787e4bdaed01899a1..e30ecbc6feba07d49bec5c27204b45418c096ccf 100644 (file)
@@ -278,7 +278,7 @@ WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT
 -- Look for illegal values in pg_class fields
 SELECT p1.oid, p1.relname
 FROM pg_class as p1
-WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v');
+WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v', 'f');
  oid | relname 
 -----+---------
 (0 rows)
index d89b26d1fc6d007a95224d9c9638b8543c0a524a..5cbafa5e2ddbff248a7a4e063750af2bd0bc3cc0 100644 (file)
@@ -20,11 +20,13 @@ CREATE ROLE regress_test_role_super SUPERUSER;
 CREATE ROLE regress_test_indirect;
 CREATE ROLE unprivileged_role;
 
+CREATE SCHEMA foreign_schema;
+
 CREATE FOREIGN DATA WRAPPER dummy;
 CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
 
 -- At this point we should have 2 built-in wrappers and no servers.
-SELECT fdwname, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
+SELECT fdwname, fdwvalidator::regproc, fdwhandler::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY fdwname;
 SELECT srvname, srvoptions FROM pg_foreign_server;
 SELECT * FROM pg_user_mapping;
 
@@ -147,6 +149,8 @@ CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
 RESET ROLE;
 REVOKE regress_test_indirect FROM regress_test_role;
 
+CREATE SERVER sc FOREIGN DATA WRAPPER dummy;
+
 -- ALTER SERVER
 ALTER SERVER s0;                                            -- ERROR
 ALTER SERVER s0 OPTIONS (a '1');                            -- ERROR
@@ -219,6 +223,7 @@ CREATE USER MAPPING FOR current_user SERVER s6 OPTIONS (username 'test');
 CREATE USER MAPPING FOR current_user SERVER s7;             -- ERROR
 CREATE USER MAPPING FOR public SERVER s8;                   -- ERROR
 RESET ROLE;
+CREATE USER MAPPING FOR public SERVER sc;
 
 ALTER SERVER t1 OWNER TO regress_test_indirect;
 SET ROLE regress_test_role;
@@ -254,6 +259,77 @@ RESET ROLE;
 DROP SERVER s7;
 \deu
 
+-- CREATE FOREIGN TABLE
+CREATE TABLE t1 (
+   c1 integer NOT NULL,
+   c2 text,
+   c3 date CHECK (c3 > '2000-01-01'::date),
+   CONSTRAINT t1_c1_check CHECK (c1 > 0)
+);
+CREATE TABLE t2 ();
+CREATE TABLE f1
+CREATE FOREIGN TABLE ft1 ();                                    -- ERROR
+CREATE FOREIGN TABLE ft1 () SERVER no_server;                   -- ERROR
+CREATE FOREIGN TABLE ft1 () INHERITS () SERVER sc;              -- ERROR
+CREATE FOREIGN TABLE ft1 () INHERITS (no_table) SERVER sc;      -- ERROR
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER sc;                 -- ERROR
+CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;                -- ERROR
+CREATE FOREIGN TABLE ft1 (
+   c1 integer NOT NULL,
+   c2 text,
+   c3 date CHECK (c3 > '2000-01-01'::date),
+   CONSTRAINT ft1_c1_check CHECK (c1 > 0)
+) SERVER sc OPTIONS (delimiter ',', quote '"');
+\d+ ft1
+CREATE FOREIGN TABLE ft2 () INHERITS (t1) SERVER sc OPTIONS (delimiter ' ', quote '`');
+\d+ ft2
+\det+
+CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
+
+-- ALTER FOREIGN TABLE
+COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
+COMMENT ON FOREIGN TABLE ft1 IS NULL;
+COMMENT ON COLUMN ft1.c1 IS 'foreign column';
+COMMENT ON COLUMN ft1.c1 IS NULL;
+
+ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0;        -- ERROR
+ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
+
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0;          -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT;           -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const;               -- ERROR
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ALTER FOREIGN TABLE ft1 SET WITH OIDS;                          -- ERROR
+ALTER FOREIGN TABLE ft1 INHERIT t2;
+ALTER FOREIGN TABLE ft1 NO INHERIT t2;
+ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
+ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@');
+ALTER FOREIGN TABLE ft1 DROP COLUMN no_column;                  -- ERROR
+ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column;
+ALTER FOREIGN TABLE ft1 DROP COLUMN c9;
+ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema;
+ALTER FOREIGN TABLE ft1 SET TABLESPACE ts;                      -- ERROR
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
+\d foreign_schema.foreign_table_1
+
+-- DROP FOREIGN TABLE
+DROP FOREIGN TABLE no_table;                                    -- ERROR
+DROP FOREIGN TABLE IF EXISTS no_table;
+DROP FOREIGN TABLE foreign_schema.foreign_table_1;
+
 -- Information schema
 
 SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
@@ -264,6 +340,8 @@ SELECT * FROM information_schema.user_mappings ORDER BY lower(authorization_iden
 SELECT * FROM information_schema.user_mapping_options ORDER BY lower(authorization_identifier), 2, 3, 4;
 SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5;
 SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5;
+SELECT * FROM information_schema.foreign_tables ORDER BY 1, 2, 3;
+SELECT * FROM information_schema.foreign_table_options ORDER BY 1, 2, 3, 4;
 SET ROLE regress_test_role;
 SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4;
 SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5;
@@ -367,6 +445,9 @@ DROP SERVER s9 CASCADE;                                         -- ERROR
 RESET ROLE;
 
 -- Cleanup
+DROP SCHEMA foreign_schema CASCADE;
+DROP TABLE t1 CASCADE;
+DROP TABLE t2 CASCADE;
 DROP ROLE regress_test_role;                                -- ERROR
 DROP SERVER s5 CASCADE;
 DROP SERVER t1 CASCADE;
@@ -386,6 +467,6 @@ DROP FOREIGN DATA WRAPPER dummy CASCADE;
 DROP ROLE foreign_data_user;
 
 -- At this point we should have no wrappers, no servers, and no mappings.
-SELECT fdwname, fdwvalidator, fdwoptions FROM pg_foreign_data_wrapper;
+SELECT fdwname, fdwvalidator, fdwhandler, fdwoptions FROM pg_foreign_data_wrapper;
 SELECT srvname, srvoptions FROM pg_foreign_server;
 SELECT * FROM pg_user_mapping;
index af7aa2d8b3da24b91fabfb6a7a04f081341f1d58..fa6dd75f07fd4066f12fad76fb651c07b9c35e9d 100644 (file)
@@ -217,7 +217,7 @@ WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT
 
 SELECT p1.oid, p1.relname
 FROM pg_class as p1
-WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v');
+WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v', 'f');
 
 -- Indexes should have an access method, others not.