Import foreign_table patch.
authorShigeru Hanada <hanada@metrosystems.co.jp>
Tue, 28 Sep 2010 09:04:18 +0000 (18:04 +0900)
committerShigeru Hanada <hanada@metrosystems.co.jp>
Thu, 30 Sep 2010 04:31:56 +0000 (13:31 +0900)
61 files changed:
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_table.sgml [new file with mode: 0644]
doc/src/sgml/ref/comment.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/comment.c
src/backend/commands/foreigncmds.c
src/backend/commands/seclabel.c
src/backend/commands/tablecmds.c
src/backend/executor/execMain.c
src/backend/foreign/foreign.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/backend/rewrite/rewriteDefine.c
src/backend/tcop/utility.c
src/backend/utils/adt/acl.c
src/backend/utils/cache/syscache.c
src/bin/pg_dump/common.c
src/bin/pg_dump/dumputils.c
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_dump.c
src/bin/psql/command.c
src/bin/psql/describe.c
src/bin/psql/describe.h
src/bin/psql/help.c
src/include/catalog/dependency.h
src/include/catalog/indexing.h
src/include/catalog/pg_class.h
src/include/catalog/pg_foreign_table.h [new file with mode: 0644]
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/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 8e4081cb33c33744804b1f7afaa2b0c131a7620a..3eeecb9385f04e9a12279ba75dbbb79b0761dfb4 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>
 
  </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 509efea8e197ea5f75e607ed23feadd9363f1659..dd08c4da4c84d25c7c77aab2e52a0b5f0b623c24 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 f5d67a207876cd40f64f4736575409e06cfa4b4b..92be3377472d53b8262ce1b12abfcba26d332d76 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">
@@ -51,6 +52,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">
@@ -86,6 +88,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 e1aa293c7f8fe99d89a47d38a99c61da68474ae8..612645d4a4b661bb8b76d0b07c8bd1d27e733390 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>
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..c0719e1
--- /dev/null
@@ -0,0 +1,556 @@
+<!--
+$PostgreSQL$
+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 DEFAULT <replaceable class="PARAMETER">expression</replaceable>
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
+    ADD <replaceable class="PARAMETER">table_constraint</replaceable>
+    DROP CONSTRAINT [ IF EXISTS ]  <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
+    DISABLE RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
+    ENABLE RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
+    ENABLE REPLICA RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
+    ENABLE ALWAYS RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
+    SET WITH OIDS
+    SET WITHOUT OIDS
+    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-CREATETABLE">.
+     </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>DISABLE</literal>/<literal>ENABLE [ REPLICA | ALWAYS ] RULE</literal></term>
+    <listitem>
+     <para>
+      These forms configure the firing of rewrite rules belonging to the table.
+      A disabled rule is still known to the system, but is not applied
+      during query rewriting.
+      This configuration is ignored for <literal>ON SELECT</literal> rules, which
+      are always applied in order to keep views working even if the current
+      session is in a non-default replication role.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET WITH OIDS</literal></term>
+    <listitem>
+     <para>
+      This form adds an <literal>oid</literal> system column to the
+      table (see <xref linkend="ddl-system-columns">).
+      It does nothing if the table already has OIDs.
+     </para>
+
+     <para>
+      Note that this is not equivalent to <literal>ADD COLUMN oid oid</>;
+      that would add a normal column that happened to be named
+      <literal>oid</>, not a system column.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET WITHOUT OIDS</literal></term>
+    <listitem>
+     <para>
+      This form removes the <literal>oid</literal> system column from the
+      table.  This is exactly equivalent to
+      <literal>DROP COLUMN oid RESTRICT</literal>,
+      except that it will not complain if there is already no
+      <literal>oid</literal> column.
+     </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>
+    </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.
+      <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 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 b5f7e3196ce548e40e9ba5295fa8cbdc06493eb3..a14e292590a52afb20f77a41b5e91a44d6aef35a 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';
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..d012298
--- /dev/null
@@ -0,0 +1,519 @@
+<!--
+$PostgreSQL$
+PostgreSQL documentation
+-->
+
+<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> [ <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>
+[ WITH OIDS | WITHOUT OIDS ]
+[ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
+
+CREATE FOREIGN TABLE <replaceable class="PARAMETER">table_name</replaceable>
+    OF <replaceable class="PARAMETER">type_name</replaceable> [ (
+  { <replaceable class="PARAMETER">column_name</replaceable> WITH OPTIONS [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+    | <replaceable>table_constraint</replaceable> }
+    [, ... ]
+) ]
+[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
+  SERVER <replaceable class="parameter">server_name</replaceable>
+[ WITH OIDS | WITHOUT OIDS ]
+[ 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 } { DEFAULTS | 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>
+
+ </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><literal>OF <replaceable class="PARAMETER">type_name</replaceable></literal></term>
+    <listitem>
+     <para>
+      Creates a <firstterm>typed table</firstterm>, which takes its
+      structure from the specified composite type (name optionally
+      schema-qualified).  A typed table is tied to its type; for
+      example the table will be dropped if the type is dropped
+      (with <literal>DROP TYPE ... CASCADE</literal>).
+     </para>
+
+     <para>
+      When a typed table is created, then the data types of the
+      columns are determined by the underlying composite type and are
+      not specified by the <literal>CREATE FOREIGN TABLE</literal> command.
+      But the <literal>CREATE FOREIGN TABLE</literal> command can add defaults
+      and constraints to the table.
+     </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>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>
+      Column <literal>STORAGE</> settings are also copied from parent tables.
+     </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>
+      Default expressions for the copied column definitions will only be
+      copied if <literal>INCLUDING DEFAULTS</literal> is specified.  The
+      default behavior is to exclude default expressions, resulting in the
+      copied columns in the new table having null defaults.
+     </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 DEFAULTS 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>
+
+   <varlistentry>
+    <term><literal>WITH OIDS | WITHOUT OIDS</literal></term>
+    <listitem>
+     <para>
+      This clause specifies whether rows of the foreign table should have
+      OIDs (object identifiers) assigned to them, or not.
+      If <literal>WITH OIDS</> is not specified, the default setting depends
+      upon the <xref linkend="guc-default-with-oids"> configuration parameter.
+      (If the new foreign table inherits from any tables that have OIDs, then
+      <literal>WITH OIDS</> is forced even if the command says
+      <literal>WITHOUT OIDS</>.)
+     </para>
+
+     <para>
+      If <literal>WITH OIDS</literal> is specified or implied, the first
+      column of the rows which was retrieved from the foreign table
+      must be the oid column.
+     </para>
+
+     <para>
+      To remove OIDs from a foreign table after it has been created, use <xref
+      linkend="sql-alterforeigntable">.
+     </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>
+
+  <para>
+   Create a composite type and a typed foreign table:
+<programlisting>
+CREATE TYPE employee_type AS (name text, salary numeric);
+
+CREATE FOREIGN TABLE employees OF employee_type (
+    salary WITH OPTIONS CHECK (salary &gt; 0)
+)
+SERVER employee_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 generic options</title>
+
+   <para>
+    The SQL standard says that column definition can contain
+    column generic options, but currently column generic options are
+    not supported.
+   </para>
+  </refsect2>
+
+  <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>
+
+  <refsect2>
+   <title><literal>WITH</> clause</title>
+
+   <para>
+    The <literal>WITH</> clause is a <productname>PostgreSQL</productname>
+    extension; neither storage parameters nor OIDs are in the standard.
+   </para>
+  </refsect2>
+
+  <refsect2>
+   <title>Typed Tables</title>
+
+   <para>
+    Typed tables implement a subset of the SQL standard.  According to
+    the standard, a typed table has columns corresponding to the
+    underlying composite type as well as one other column that is
+    the <quote>self-referencing column</quote>.  PostgreSQL does not
+    support these self-referencing columns explicitly, but the same
+    effect can be had using the OID feature.
+   </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 3298c0ad27a75e3b045d6a9bbcb0cade80319203..89d802eada495d480dd9304a54b3029ec4f1c68c 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 48e2e0b96313729cfbf5dd8cd4312ba9e26eae75..a69c6d39e3786930b98ca1c2cd81c63a169abc5b 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..1b3af5b
--- /dev/null
@@ -0,0 +1,123 @@
+<!--
+$PostgreSQL$
+PostgreSQL documentation
+-->
+
+<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 rules
+   and 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 eb716cf6f09686fa0dba9283b40ec8db77b0f1bb..68f2db2ad1aa26d1916c65b1e75783b9c02e8c74 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.
+        Associated indexes, constraints, rules, triggers are
+        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.)
@@ -1034,6 +1036,20 @@ testdb=&gt;
       </varlistentry>
 
 
+      <varlistentry>
+        <term><literal>\det[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        For each foreign table (mnemonic: <quote>external
+        tables</quote>) matching the
+        <replaceable class="parameter">pattern</replaceable>, show name
+        and associated foreign server.  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>
index 463746cda3a0553cb43c3ef76623aaa055fd7327..0a481766ebc94f3bb1516f9e50a912f43d90c89c 100644 (file)
@@ -40,6 +40,7 @@
    &alterDefaultPrivileges;
    &alterDomain;
    &alterForeignDataWrapper;
+   &alterForeignTable;
    &alterFunction;
    &alterGroup;
    &alterIndex;
@@ -79,6 +80,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 fb080e9298ffe4dbae5d243d34d3d4ab2676dd01..fcc42b833f09610f685bc357c667290e8dd0f699 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);
@@ -1683,13 +1694,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;
@@ -1723,6 +1750,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))
@@ -1775,9 +1812,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;
@@ -1806,12 +1852,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.
@@ -1819,9 +1879,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);
 
@@ -1903,6 +1961,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,
@@ -3074,7 +3142,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] =
@@ -3114,7 +3184,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"),
 };
 
 
@@ -3404,9 +3476,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 18e07eb956a4f6eb4a4fcfd6bb8f0523996a984d..04607ccb68eca742b00783b8480fcafcb476901d 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 */
 };
 
@@ -2079,6 +2081,10 @@ getObjectClass(const ObjectAddress *object)
            Assert(object->objectSubId == 0);
            return OCLASS_USER_MAPPING;
 
+       case ForeignTableRelationId:
+           Assert(object->objectSubId == 0);
+           return OCLASS_FOREIGN_TABLE;
+
        case DefaultAclRelationId:
            Assert(object->objectSubId == 0);
            return OCLASS_DEFACL;
@@ -2747,6 +2753,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 dcc53e13a1a7523128aa7622c47df5c283f9374b..55321557f075860f6ac69377f063b4686e3fd345 100644 (file)
@@ -42,6 +42,7 @@
 #include "catalog/namespace.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"
@@ -267,6 +268,7 @@ heap_create(const char *relname,
    {
        case RELKIND_VIEW:
        case RELKIND_COMPOSITE_TYPE:
+       case RELKIND_FOREIGN_TABLE:
            create_storage = false;
 
            /*
@@ -769,7 +771,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;
@@ -983,7 +985,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;
@@ -1009,6 +1012,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;
@@ -1045,7 +1049,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 ||
@@ -1563,11 +1567,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 bd812220f14c81d1459a7dfb2446afda30792823..c94726149003cc04dd6cdda0fcaada45cc7c7047 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')
@@ -2128,7 +2129,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');
@@ -2198,7 +2199,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;
@@ -2329,7 +2330,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
@@ -2466,6 +2467,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 93f5ac6beee9ade08f2ea7495f48c214daf81d40..1f0d279194de2efff1cafb4458c5d062eb43de95 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 794d355944d3bec337deef3f4fa59336587c2fd3..8b22940c22567b6779b3008fff10aeba806a35a2 100644 (file)
@@ -91,6 +91,7 @@ ExecRenameStmt(RenameStmt *stmt)
        case OBJECT_COLUMN:
        case OBJECT_ATTRIBUTE:
        case OBJECT_TRIGGER:
+       case OBJECT_FOREIGN_TABLE:
            {
                Oid         relid;
 
@@ -104,6 +105,7 @@ ExecRenameStmt(RenameStmt *stmt)
                    case OBJECT_SEQUENCE:
                    case OBJECT_VIEW:
                    case OBJECT_INDEX:
+                   case OBJECT_FOREIGN_TABLE:
                        {
                            /*
                             * RENAME TABLE requires that we (still) hold
@@ -190,6 +192,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 456e8a2b377b362cf15eea6c42f1992c606b5b5e..c0311d38f4e4237d79b22842a6ad35e7d6960efc 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 4e6367c5268c05a36a707a0781e30799a3469833..ea39403b9a71fba7c580d486482e62474b9bc3ff 100644 (file)
@@ -20,6 +20,7 @@
 #include "catalog/indexing.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 "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"
 
 
 /*
@@ -1147,3 +1150,151 @@ RemoveUserMappingById(Oid umId)
 
    heap_close(rel, RowExclusiveLock);
 }
+
+/*
+ * Create a foreign table
+ * call after DefineRelation().
+ */
+void
+CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
+{
+   Relation    rel;
+   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;
+
+   /*
+    * 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);
+
+   /*
+    * Insert tuple into pg_foreign_table.
+    */
+   rel = 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 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(rel->rd_att, values, nulls);
+
+   /* pg_foreign_table don't have OID */
+   ftId = simple_heap_insert(rel, tuple);
+
+   CatalogUpdateIndexes(rel, 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(rel, NoLock);
+}
+
+/*
+ * ALTER FOREIGN TABLE <name> OPTIONS (...)
+ */
+void
+ATExecGenericOptions(Relation rel, List *options)
+{
+   ForeignTable       *table;
+   ForeignServer      *server;
+   ForeignDataWrapper *fdw;
+   HeapTuple           fttp;
+   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;
+
+   fttp = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
+   if (!HeapTupleIsValid(fttp))
+       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,
+                           fttp,
+                           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 */
+
+   rel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+   fttp = heap_modify_tuple(fttp, RelationGetDescr(rel),
+                          repl_val, repl_null, repl_repl);
+
+   simple_heap_update(rel, &fttp->t_self, fttp);
+   CatalogUpdateIndexes(rel, fttp);
+
+   heap_close(rel, RowExclusiveLock);
+   heap_freetuple(fttp);
+}
index 417ad88d5576d22092365c62b7f947e3bea0d4bd..1147baedb3c8f07bcf820b56003567fb9f7ba38d 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 a5d7af604209c9fa9bc23a981f402011bf2cba5f..9ff08505995b7d9534bbc328f4224bcadfc291a7 100644 (file)
@@ -28,6 +28,7 @@
 #include "catalog/namespace.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"
@@ -47,6 +48,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"
@@ -218,6 +220,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}
 };
 
@@ -263,7 +271,8 @@ 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);
@@ -667,7 +676,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)
@@ -701,6 +711,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);
@@ -1994,10 +2008,11 @@ renameatt(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))));
 
    /*
@@ -2414,6 +2429,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);
    }
@@ -2499,6 +2522,7 @@ AlterTableGetLockLevel(List *cmds)
            case AT_SetTableSpace:      /* must rewrite heap */
            case AT_DropNotNull:        /* may change some SQL plans */
            case AT_SetNotNull:
+           case AT_GenericOptions:     /* may change any of foreign access */
                cmd_lockmode = AccessExclusiveLock;
                break;
 
@@ -2657,14 +2681,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, 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, cmd, lockmode);
            pass = AT_PASS_ADD_COL;
@@ -2677,19 +2701,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;
@@ -2707,25 +2731,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(rel, recurse, cmd);
            /* 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)
@@ -2733,7 +2757,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)
@@ -2741,7 +2765,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;
@@ -2753,20 +2777,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, true);
            /* 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, true);
            /* Performs own recursion */
            if (rel->rd_rel->relhasoids)
            {
@@ -2793,7 +2817,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;
@@ -2806,12 +2830,18 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
        case AT_DisableTrig:    /* DISABLE TRIGGER variants */
        case AT_DisableTrigAll:
        case AT_DisableTrigUser:
+           ATSimplePermissions(rel, false, false, false);
+           /* These commands never recurse */
+           /* No command-specific prep needed */
+           pass = AT_PASS_MISC;
+           break;
        case AT_EnableRule:     /* ENABLE/DISABLE RULE variants */
        case AT_EnableAlwaysRule:
        case AT_EnableReplicaRule:
        case AT_DisableRule:
        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;
@@ -3058,6 +3088,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);
@@ -3084,6 +3117,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.
@@ -3232,6 +3269,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
    BulkInsertState bistate;
    int         hi_options;
 
+
    /*
     * Open the relation(s).  We have surely already locked the existing
     * table.
@@ -3532,36 +3570,36 @@ ATGetQueueEntry(List **wqueue, Relation rel)
 /*
  * ATSimplePermissions
  *
- * - Ensure that it is a relation (or possibly a view)
+ * - Ensure that it is a relation (or possibly a view or composite type or
+ *   foreign table)
  * - Ensure this user is the owner
  * - 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)
 {
    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)
-       {
-           if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+       int             relkind = rel->rd_rel->relkind;
+       StringInfoData  allowed;
+
+       /* what kind of object is allowed ? */
+       initStringInfo(&allowed);
+       appendStringInfo(&allowed, "a 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))
                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 %s",
+                               RelationGetRelationName(rel), allowed.data)));
+
+       pfree(allowed.data);
    }
 
    /* Permissions checks */
@@ -4626,7 +4664,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
@@ -4930,7 +4968,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
@@ -5873,7 +5911,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);
 
@@ -6743,6 +6781,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:
@@ -6799,7 +6838,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))));
    }
 
@@ -7456,7 +7495,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 (parent_rel->rd_istemp && !child_rel->rd_istemp)
@@ -8061,6 +8100,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);
    }
@@ -8070,6 +8117,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:
@@ -8100,7 +8148,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 20af966ddb0e4d7dc1194c745956dd69b3b9513c..864b213991413920d58a19565b5d86a78707af86 100644 (file)
@@ -904,6 +904,12 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
                     errmsg("cannot change view \"%s\"",
                            RelationGetRelationName(resultRelationDesc))));
            break;
+       case RELKIND_FOREIGN_TABLE:
+           ereport(ERROR,
+                   (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                    errmsg("cannot change foreign table \"%s\"",
+                           RelationGetRelationName(resultRelationDesc))));
+           break;
        default:
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
index cc67bcb5a5cfe450e12d744689193cc449c07c34..153ddd7ad7d4b276e7e461b7fbf39fada1ee515c 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"
@@ -130,6 +131,8 @@ GetForeignServer(Oid serverid)
 
    server = palloc(sizeof(ForeignServer));
    server->serverid = serverid;
+   server->fdwid = serverform->srvfdw;
+   server->owner = serverform->srvowner;
    server->servername = pstrdup(NameStr(serverform->srvname));
    server->owner = serverform->srvowner;
    server->fdwid = serverform->srvfdw;
@@ -414,3 +417,60 @@ 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
+is_foreign_table(Oid relid)
+{
+   HeapTuple tup;
+   char relkind;
+   bool isnull;
+
+   tup = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0);
+   relkind = CharGetDatum(
+       SysCacheGetAttr(RELOID, tup, Anum_pg_class_relkind, &isnull));
+   Assert(!isnull);
+   ReleaseSysCache(tup);
+
+   return (relkind == RELKIND_FOREIGN_TABLE);
+}
index 5bd0ef07502fca76d8ac18f43cda1c7241a2aabb..adea1469f366f1b1e6bdbfd55195e77cea6936a6 100644 (file)
@@ -3234,6 +3234,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)
 {
@@ -4123,6 +4142,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 c7dd42d13453d1531b1cd173339a2b6b2b7ffaba..6b048bdf7e9473ad7c4f7f6e2754a9fa1f3a0c3f 100644 (file)
@@ -1691,6 +1691,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)
 {
@@ -2787,6 +2800,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 b7cf0b815b7d3dff2b3e0d60c981a7d8751d8a0d..63f17ce3e3260336490d6de13c1a50c4957143e4 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <math.h>
 
+#include "foreign/foreign.h"
 #include "nodes/nodeFuncs.h"
 #ifdef OPTIMIZER_DEBUG
 #include "nodes/print.h"
@@ -254,6 +255,13 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
     * least one dimension of cost or sortedness.
     */
 
+   /*
+    * TODO: Implement foreign table scanning and replace this check with
+    * path creation for foreign table.
+    */
+   if (is_foreign_table(rte->relid))
+       elog(ERROR, "foreign table scan not implemented.");
+
    /* Consider sequential scan */
    add_path(rel, create_seqscan_path(root, rel));
 
index 4054cb1bc7bc0efa65f9bad4b9901911d1928112..78db97bdf5818c7fe3952e79ac873133a2875248 100644 (file)
@@ -185,6 +185,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
        AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt 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_foreign_table_cmd alter_type_cmd
+%type <list>   alter_table_cmds alter_foreign_table_cmds alter_type_cmds
 
 %type <dbehavior>  opt_drop_behavior
 
@@ -279,6 +281,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <list>   stmtblock stmtmulti
                OptTableElementList TableElementList OptInherit definition
                OptTypedTableElementList TypedTableElementList
+               OptForeignTableElementList ForeignTableElementList
+               OptTypedForeignTableElementList TypedForeignTableElementList
                reloptions opt_reloptions
                OptWith opt_distinct opt_definition func_args func_args_list
                func_args_with_defaults func_args_with_defaults_list
@@ -350,7 +354,12 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <vsetstmt> set_rest SetResetClause
 
 %type <node>   TableElement TypedTableElement ConstraintElem TableFuncElement
+               ForeignTableElement TypedForeignTableElement
+               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
@@ -410,10 +419,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
@@ -654,6 +668,7 @@ stmt :
            | AlterDomainStmt
            | AlterFdwStmt
            | AlterForeignServerStmt
+           | AlterForeignTableStmt
            | AlterFunctionStmt
            | AlterGroupStmt
            | AlterObjectSchemaStmt
@@ -682,6 +697,7 @@ stmt :
            | CreateDomainStmt
            | CreateFdwStmt
            | CreateForeignServerStmt
+           | CreateForeignTableStmt
            | CreateFunctionStmt
            | CreateGroupStmt
            | CreateOpClassStmt
@@ -1764,6 +1780,7 @@ alter_table_cmd:
                    n->subtype = AT_DropOids;
                    $$ = (Node *)n;
                }
+
            /* ALTER TABLE <name> CLUSTER ON <indexname> */
            | CLUSTER ON name
                {
@@ -2451,20 +2468,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);
@@ -2485,15 +2490,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);
@@ -2519,6 +2516,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
@@ -2596,6 +2622,31 @@ 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:
+           DEFAULTS            { $$ = CREATE_TABLE_LIKE_DEFAULTS; }
+           | 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.
@@ -2613,7 +2664,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);
@@ -2628,7 +2704,10 @@ ConstraintElem:
                                 parser_errposition(@5)));
                    $$ = (Node *)n;
                }
-           | UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
+       ;
+
+UniqueConstraintElem:
+           UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
                ConstraintAttributeSpec
                {
                    Constraint *n = makeNode(Constraint);
@@ -2641,7 +2720,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);
@@ -2654,7 +2736,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
                {
@@ -2670,7 +2755,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);
@@ -3364,6 +3451,346 @@ 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 OptWith create_generic_options
+               {
+                   CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt);
+                   $4->istemp = false;
+                   n->base.relation = $4;
+                   n->base.tableElts = $5;
+                   n->base.inhRelations = $6;
+                   n->base.options = $9;
+                   /* FDW-specific data */
+                   n->servername = $8;
+                   n->options = $10;
+                   $$ = (Node *) n;
+               }
+       | CREATE FOREIGN TABLE qualified_name OF any_name
+           OptTypedForeignTableElementList
+           SERVER name OptWith create_generic_options
+               {
+                   CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt);
+                   $4->istemp = false;
+                   n->base.relation = $4;
+                   n->base.tableElts = $7;
+                   n->base.ofTypename = makeTypeNameFromNameList($6);
+                   n->base.ofTypename->location = @6;
+                   n->base.options = $10;
+                   /* FDW-specific data */
+                   n->servername = $9;
+                   n->options = $11;
+                   $$ = (Node *) n;
+               }
+       ;
+
+OptForeignTableElementList:
+           '(' ForeignTableElementList ')'         { $$ = $2; }
+           | '(' ')'                               { $$ = NIL; }
+           | /*EMPTY*/
+               {
+                   /* TODO: import definitions from foreign server in this case. */
+                   $$ = NIL;
+               }
+       ;
+
+OptTypedForeignTableElementList:
+           '(' TypedForeignTableElementList ')'    { $$ = $2; }
+           | /*EMPTY*/                             { $$ = NIL; }
+       ;
+
+ForeignTableElementList:
+           ForeignTableElement
+               {
+                   $$ = list_make1($1);
+               }
+           | ForeignTableElementList ',' ForeignTableElement
+               {
+                   $$ = lappend($1, $3);
+               }
+       ;
+
+TypedForeignTableElementList:
+           TypedForeignTableElement
+               {
+                   $$ = list_make1($1);
+               }
+           | TypedForeignTableElementList ',' TypedForeignTableElement
+               {
+                   $$ = lappend($1, $3);
+               }
+       ;
+
+ForeignTableElement:
+           foreignColumnDef                    { $$ = $1; }
+           | ForeignTableLikeClause            { $$ = $1; }
+           | ForeignTableConstraint            { $$ = $1; }
+       ;
+
+TypedForeignTableElement:
+           columnOptions                       { $$ = $1; }
+           | ForeignTableConstraint            { $$ = $1; }
+       ;
+
+/* TODO: support GENERIC OPTIONS of foreign column */
+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; }
+           | DEFAULT b_expr
+               {
+                   Constraint *n = makeNode(Constraint);
+                   n->contype = CONSTR_DEFAULT;
+                   n->location = @1;
+                   n->raw_expr = $2;
+                   n->cooked_expr = NULL;
+                   $$ = (Node *)n;
+               }
+       ;
+
+ForeignColConstraintElem:
+           ColNotNullConstraintElem                { $$ = $1; }
+           | ColNullConstraintElem                 { $$ = $1; }
+           | ColCheckConstraintElem                { $$ = $1; }
+       ;
+
+/*****************************************************************************
+ *
+ *         QUERY:
+ *             ALTER FORIGN TABLE relname ADD [COLUMN] column-definition;
+ *             ALTER FORIGN TABLE relname ALTERR [COLUMN] name OPTIONS (...);
+ *             ALTER FORIGN TABLE relname DROP [COLUMN] name [CASCADE];
+ *             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 columnDef
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_AddColumn;
+                   n->def = $2;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> ADD COLUMN <coldef> */
+           | ADD_P COLUMN columnDef
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_AddColumn;
+                   n->def = $3;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> {SET DEFAULT <expr>|DROP DEFAULT} */
+           | ALTER opt_column ColId alter_column_default
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_ColumnDefault;
+                   n->name = $3;
+                   n->def = $4;
+                   $$ = (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> SET WITH OIDS  */
+           | SET WITH OIDS
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_AddOids;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> SET WITHOUT OIDS  */
+           | SET WITHOUT OIDS
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_DropOids;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> ENABLE RULE <rule> */
+           | ENABLE_P RULE name
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_EnableRule;
+                   n->name = $3;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> ENABLE ALWAYS RULE <rule> */
+           | ENABLE_P ALWAYS RULE name
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_EnableAlwaysRule;
+                   n->name = $4;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> ENABLE REPLICA RULE <rule> */
+           | ENABLE_P REPLICA RULE name
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_EnableReplicaRule;
+                   n->name = $4;
+                   $$ = (Node *)n;
+               }
+           /* ALTER FOREIGN TABLE <name> DISABLE RULE <rule> */
+           | DISABLE_P RULE name
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_DisableRule;
+                   n->name = $3;
+                   $$ = (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:
@@ -4135,6 +4562,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:
@@ -4186,8 +4614,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) |
@@ -4364,6 +4792,7 @@ comment_type:
            | CONVERSION_P                      { $$ = OBJECT_CONVERSION; }
            | TABLESPACE                        { $$ = OBJECT_TABLESPACE; }
            | ROLE                              { $$ = OBJECT_ROLE; }
+           | FOREIGN TABLE                     { $$ = OBJECT_FOREIGN_TABLE; }
        ;
 
 comment_text:
@@ -4451,6 +4880,7 @@ security_label_type:
            | DOMAIN_P                          { $$ = OBJECT_TYPE; }
            | TYPE_P                            { $$ = OBJECT_TYPE; }
            | VIEW                              { $$ = OBJECT_VIEW; }
+           | FOREIGN TABLE                     { $$ = OBJECT_FOREIGN_TABLE; }
        ;
 
 security_label:    Sconst              { $$ = $1; }
@@ -4780,6 +5210,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));
@@ -5866,6 +6304,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);
@@ -5875,6 +6322,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);
@@ -6042,6 +6498,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 37ca331c215661fde72d5e0a4bc3be613c801588..bb11a6c06b2dc2e0b01734c8e9f6f94bec65e471 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 */
@@ -172,7 +172,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 029a2888a72871ef3203bd1d3d5ce9efdf27ca68..2823f97a46fa249fc6c018320d0968e9df63d456 100644 (file)
@@ -249,10 +249,11 @@ DefineQueryRewrite(char *rulename,
     * Verify relation is of a type that rules can sensibly be applied to.
     */
    if (event_relation->rd_rel->relkind != RELKIND_RELATION &&
-       event_relation->rd_rel->relkind != RELKIND_VIEW)
+       event_relation->rd_rel->relkind != RELKIND_VIEW &&
+       event_relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                errmsg("\"%s\" is not a table or view",
+                errmsg("\"%s\" is not a table, view or foreign table",
                        RelationGetRelationName(event_relation))));
 
    if (!allowSystemTableMods && IsSystemRelation(event_relation))
@@ -293,6 +294,14 @@ DefineQueryRewrite(char *rulename,
 
    if (event_type == CMD_SELECT)
    {
+       /* Foreign tables can't be changed to view. */
+       if (event_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+       {
+           ereport(ERROR,
+                   (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                    errmsg("cannot define rule on SELECT to a foreign table")));
+       }
+
        /*
         * Rules ON SELECT are restricted to view definitions
         *
index 75cb354ea89db9ce071ab3d781c8daf3280cd5e4..dd27e35d6281c51c3efb32c1925827e506324984 100644 (file)
@@ -219,6 +219,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;
@@ -491,6 +492,7 @@ standard_ProcessUtility(Node *parsetree,
            break;
 
        case T_CreateStmt:
+       case T_CreateForeignTableStmt:
            {
                List       *stmts;
                ListCell   *l;
@@ -539,6 +541,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 */
@@ -617,6 +629,7 @@ standard_ProcessUtility(Node *parsetree,
                    case OBJECT_SEQUENCE:
                    case OBJECT_VIEW:
                    case OBJECT_INDEX:
+                   case OBJECT_FOREIGN_TABLE:
                        RemoveRelations(stmt);
                        break;
 
@@ -1546,6 +1559,10 @@ CreateCommandTag(Node *parsetree)
            tag = "DROP USER MAPPING";
            break;
 
+       case T_CreateForeignTableStmt:
+           tag = "CREATE FOREIGN TABLE";
+           break;
+
        case T_DropStmt:
            switch (((DropStmt *) parsetree)->removeType)
            {
@@ -1585,6 +1602,9 @@ CreateCommandTag(Node *parsetree)
                case OBJECT_TSCONFIGURATION:
                    tag = "DROP TEXT SEARCH CONFIGURATION";
                    break;
+               case OBJECT_FOREIGN_TABLE:
+                   tag = "DROP FOREIGN TABLE";
+                   break;
                default:
                    tag = "???";
            }
@@ -1655,6 +1675,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;
@@ -1713,6 +1736,9 @@ CreateCommandTag(Node *parsetree)
                case OBJECT_VIEW:
                    tag = "ALTER VIEW";
                    break;
+               case OBJECT_FOREIGN_TABLE:
+                   tag = "ALTER FOREIGN TABLE";
+                   break;
                default:
                    tag = "???";
                    break;
@@ -1773,6 +1799,9 @@ CreateCommandTag(Node *parsetree)
                case OBJECT_FOREIGN_SERVER:
                    tag = "ALTER SERVER";
                    break;
+               case OBJECT_FOREIGN_TABLE:
+                   tag = "ALTER FOREIGN TABLE";
+                   break;
                default:
                    tag = "???";
                    break;
@@ -1797,6 +1826,9 @@ CreateCommandTag(Node *parsetree)
                case OBJECT_VIEW:
                    tag = "ALTER VIEW";
                    break;
+               case OBJECT_FOREIGN_TABLE:
+                   tag = "ALTER FOREIGN TABLE";
+                   break;
                default:
                    tag = "???";
                    break;
@@ -2289,6 +2321,7 @@ GetCommandLogLevel(Node *parsetree)
            break;
 
        case T_CreateStmt:
+       case T_CreateForeignTableStmt:
            lev = LOGSTMT_DDL;
            break;
 
index 943f914e2bc85575ee80a17933dd947d84fd56a2..fd0db97a66b0dadd61876ad2d7cabc04b7e2ac39 100644 (file)
@@ -781,6 +781,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 94bef7dd018ec7d48164401c963e900577316407..2778d1b3fcb4aa31df44a9d03bf658c21c51b324 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 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 d1a9c54b91faba205bab7d85f73f4cbc4ac7d57e..d8c6087b6cfbd115c142b79006fdfd6bd77ec1a3 100644 (file)
@@ -2724,6 +2724,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)
    {
@@ -2916,6 +2917,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 ff7d97a064bcb98c99bdfefe145e6b80665e887d..debd1b587a65c7bf558cacc3c89bfc51dfb1e6f3 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,
@@ -10801,7 +10840,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);
@@ -10870,6 +10911,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
    int         j,
                k;
    bool        toast_set = false;
+   char       *srvname;
+   char       *ftoptions;
 
    /* Make sure we are in proper schema */
    selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
@@ -10943,7 +10986,31 @@ 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";
        numParents = tbinfo->numParents;
        parents = tbinfo->parents;
 
@@ -10951,7 +11018,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));
@@ -10959,7 +11026,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);
@@ -11094,6 +11161,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))
        {
@@ -11113,6 +11183,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
            appendPQExpBuffer(q, ")");
        }
 
+       if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+           appendPQExpBuffer(q, "\nOPTIONS (%s)", ftoptions);
+
        appendPQExpBuffer(q, ";\n");
 
        /*
@@ -11127,7 +11200,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++)
            {
@@ -11286,7 +11359,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
    ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
                 tbinfo->dobj.name,
                 tbinfo->dobj.namespace->dobj.name,
-           (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
+            (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
                 tbinfo->rolname,
               (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
                 reltypename, SECTION_PRE_DATA,
index e6d703abe740f2cb308a106b0f8013e252d2915c..e167e7a17c906395cb21c0a5dca1e79b3c0a8dec 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);
@@ -483,6 +483,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 83e9845103a4618dc06930041617f5a9cbdb5a4f..5501c178563bbee6316a22d12f9d5d1860cc1bda 100644 (file)
@@ -677,11 +677,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");
@@ -697,7 +698,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
@@ -911,15 +912,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"
@@ -1287,6 +1289,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\"",
@@ -1299,7 +1305,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");
@@ -1527,7 +1534,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;
@@ -1936,6 +1943,31 @@ describeOneTableDetails(const char *schemaname,
            PQclear(result);
        }
 
+       /* 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);
 
@@ -2345,6 +2377,7 @@ listDbRoleSettings(const char *pattern, const char *pattern2)
  * i - indexes
  * v - views
  * s - sequences
+ * e - foreign table(Note: different from 'f' for relkind)
  * (any order of the above is fine)
  * If tabtypes is empty, we default to \dtvs.
  */
@@ -2355,6 +2388,7 @@ 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;
@@ -2373,7 +2407,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"),
@@ -2382,6 +2416,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"));
 
@@ -2419,6 +2454,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");
 
@@ -3465,6 +3503,63 @@ 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 c.relname AS \"%s\",\n"
+                     "  s.srvname AS \"%s\"",
+                     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_foreign_server s\n");
+   appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
+   appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
+
+   processSQLNamePattern(pset.db, &buf, pattern, true, false,
+                         NULL, "c.relname", "s.srvname", 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 ddf4aac482be7336bcb26129611183bcc448af36..65e5ef14f79a8c1f20174be65433ffa7181adb55 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 66f72e8389172b537ea167a239dcf58ff5e9ec0f..c070b64ac50fad51795b7938b651ddd91dfe8f60 100644 (file)
@@ -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"));
index ccde371f6a17357b93ed0b91b698bda689f36f1b..dcc53be56b907e880809471bc7e92a8953d4dc9d 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 b7c9849314eb1dc2f468ea0a3c22fe2e63de2b67..8b562854a34dc077978196acc5120010c48beabe 100644 (file)
@@ -273,6 +273,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 f50cf9d55bbe9601a6e3c69d20b4b370b6e43cf5..035bb5f01150a11e23efa642210ef637d8f7a0b1 100644 (file)
@@ -148,5 +148,6 @@ 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 */
 
 #endif   /* PG_CLASS_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..2492ca1
--- /dev/null
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_foreign_table.h
+ *   definition of the system "foreign table" relation (pg_foreign_table)
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ * 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 1dc1a7d1944bc6afc01b78efb3330e5742a5fd1b..1814c8dbd89e2077d3cae66fe60c5a699a77d8c3 100644 (file)
@@ -147,6 +147,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..6cf2dcee9d207dbe8e6c03e33e3d17653b2e576a 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,7 @@ 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 */
 } GenericOptionFlags;
 
 typedef struct ForeignDataWrapper
@@ -59,6 +60,12 @@ 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;
 
 extern ForeignServer *GetForeignServer(Oid serverid);
 extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok);
@@ -68,5 +75,11 @@ 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 is_foreign_table(Oid relid);
+
+/* ALTER FOREIGN TABLE ... OPTIONS (...) handlers */
+extern void ATExecGenericOptions(Relation rel, List *options);
 
 #endif   /* FOREIGN_H */
index 0d33a2ed5ffa31a1b1073f3ddc53eb91c95a1e96..fdcdabed8a3c7c43f606c597502630bc0988739a 100644 (file)
@@ -348,6 +348,7 @@ typedef enum NodeTag
    T_DropUserMappingStmt,
    T_AlterTableSpaceOptionsStmt,
    T_SecLabelStmt,
+   T_CreateForeignTableStmt,
 
    /*
     * TAGS FOR PARSE TREE NODES (parsenodes.h)
index b2f0fef5139f30ee51c9e60033aef48cd40bbfde..a331eb36db253a05cc3e932160d84029d326cca1 100644 (file)
@@ -501,7 +501,11 @@ 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 DEFALUT/CONSTRAINT/COMMENT. */
+   CREATE_TABLE_LIKE_FOREIGN_ALL = ( CREATE_TABLE_LIKE_DEFAULTS |
+                                     CREATE_TABLE_LIKE_CONSTRAINTS |
+                                     CREATE_TABLE_LIKE_COMMENTS)
 } CreateStmtLikeOption;
 
 /*
@@ -1055,6 +1059,7 @@ typedef enum ObjectType
    OBJECT_DOMAIN,
    OBJECT_FDW,
    OBJECT_FOREIGN_SERVER,
+   OBJECT_FOREIGN_TABLE,
    OBJECT_FUNCTION,
    OBJECT_INDEX,
    OBJECT_LANGUAGE,
@@ -1154,7 +1159,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 */
@@ -1215,6 +1221,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 */
@@ -1568,6 +1575,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 30e0f8f3bd7ccb051a5b7534b936065b328d3acb..2204d0e4b11b0d172fd6acfb4890d835c105b8b5 100644 (file)
@@ -56,6 +56,7 @@ enum SysCacheIdentifier
    FOREIGNDATAWRAPPEROID,
    FOREIGNSERVERNAME,
    FOREIGNSERVEROID,
+   FOREIGNTABLEREL,
    INDEXRELID,
    LANGNAME,
    LANGOID,
index ab19a8e4fc8a9818b1f1386e513548f75452fbb1..5965b0cd40bedfe42b98808b545ceb92587cd9cc 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..00f6e058ee4b470186a1afbf3387f0daa5a0079e 100644 (file)
@@ -13,6 +13,7 @@ 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.
@@ -627,6 +628,157 @@ DROP SERVER s7;
  t1     | regress_test_role
 (8 rows)
 
+-- CREATE FOREIGN TABLE
+CREATE TABLE t1 (
+   c1 integer NOT NULL,
+   c2 text DEFAULT 'foo',
+   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 s4;              -- ERROR
+ERROR:  syntax error at or near ")"
+LINE 1: CREATE FOREIGN TABLE ft1 () INHERITS () SERVER s4;
+                                              ^
+CREATE FOREIGN TABLE ft1 () INHERITS (no_table) SERVER s4;      -- ERROR
+ERROR:  relation "no_table" does not exist
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER s4;                 -- 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 (
+   c1 integer NOT NULL,
+   c2 text DEFAULT 'foo',
+   c3 date CHECK (c3 > '2000-01-01'::date),
+   CONSTRAINT ft1_c1_check CHECK (c1 > 0)
+) SERVER s4 OPTIONS (opt1 'val1', opt2 'val2');
+CREATE RULE ft1_insert_rule AS ON INSERT TO ft1 DO INSTEAD
+   INSERT INTO t1 VALUES (new.c1, new.c2, new.c3);
+\d+ ft1
+                   Foreign table "public.ft1"
+ Column |  Type   |      Modifiers      | Storage  | Description 
+--------+---------+---------------------+----------+-------------
+ c1     | integer | not null            | plain    | 
+ c2     | text    | default 'foo'::text | extended | 
+ c3     | date    |                     | plain    | 
+Check constraints:
+    "ft1_c1_check" CHECK (c1 > 0)
+    "ft1_c3_check" CHECK (c3 > '01-01-2000'::date)
+Rules:
+    ft1_insert_rule AS
+    ON INSERT TO ft1 DO INSTEAD  INSERT INTO t1 (c1, c2, c3) 
+  VALUES (new.c1, new.c2, new.c3)
+Server: s4
+Has OIDs: no
+
+CREATE FOREIGN TABLE ft2 () INHERITS (t1) SERVER s4 WITH OIDS OPTIONS (opt1 'val1', opt2 'val2');
+\d+ ft2
+                   Foreign table "public.ft2"
+ Column |  Type   |      Modifiers      | Storage  | Description 
+--------+---------+---------------------+----------+-------------
+ c1     | integer | not null            | plain    | 
+ c2     | text    | default 'foo'::text | extended | 
+ c3     | date    |                     | plain    | 
+Check constraints:
+    "t1_c1_check" CHECK (c1 > 0)
+    "t1_c3_check" CHECK (c3 > '01-01-2000'::date)
+Server: s4
+Inherits: t1
+Has OIDs: yes
+
+\det+
+         List of foreign tables
+ Table | Server |        Options        
+-------+--------+-----------------------
+ ft1   | s4     | {opt1=val1,opt2=val2}
+ ft2   | s4     | {opt1=val1,opt2=val2}
+(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;
+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;
+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;
+ALTER FOREIGN TABLE ft1 SET WITHOUT OIDS;
+ALTER FOREIGN TABLE ft1 ENABLE RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 ENABLE ALWAYS RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 ENABLE REPLICA RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 DISABLE RULE ft1_insert_rule;
+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 opt1, SET opt2 'VAL2', ADD opt3 'val3');
+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    | default 'foo'::text
+ c3               | date    | 
+ c4               | integer | default 0
+ c5               | integer | 
+ c6               | integer | not null
+ c7               | integer | 
+ c8               | text    | 
+ c10              | integer | 
+Check constraints:
+    "ft1_c3_check" CHECK (c3 > '01-01-2000'::date)
+Disabled rules:
+    ft1_insert_rule AS
+    ON INSERT TO foreign_schema.foreign_table_1 DO INSTEAD  INSERT INTO t1 (c1, c2, c3) 
+  VALUES (new.foreign_column_1, new.c2, new.c3)
+Server: s4
+
+-- 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 
@@ -707,6 +859,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             | s4
+(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                | opt1        | val1
+ regression            | public               | ft2                | opt2        | val2
+(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 +1104,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
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 b7433653d1eebdb66415992940b00d8f43e82284..162b133582ea05de341a3ed369f5dcfa4bc98814 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..291ab7c032e856f827612808924a720e741195b7 100644 (file)
@@ -20,6 +20,8 @@ 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;
 
@@ -254,6 +256,83 @@ RESET ROLE;
 DROP SERVER s7;
 \deu
 
+-- CREATE FOREIGN TABLE
+CREATE TABLE t1 (
+   c1 integer NOT NULL,
+   c2 text DEFAULT 'foo',
+   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 s4;              -- ERROR
+CREATE FOREIGN TABLE ft1 () INHERITS (no_table) SERVER s4;      -- ERROR
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER s4;                 -- ERROR
+CREATE FOREIGN TABLE ft1 (
+   c1 integer NOT NULL,
+   c2 text DEFAULT 'foo',
+   c3 date CHECK (c3 > '2000-01-01'::date),
+   CONSTRAINT ft1_c1_check CHECK (c1 > 0)
+) SERVER s4 OPTIONS (opt1 'val1', opt2 'val2');
+CREATE RULE ft1_insert_rule AS ON INSERT TO ft1 DO INSTEAD
+   INSERT INTO t1 VALUES (new.c1, new.c2, new.c3);
+\d+ ft1
+CREATE FOREIGN TABLE ft2 () INHERITS (t1) SERVER s4 WITH OIDS OPTIONS (opt1 'val1', opt2 'val2');
+\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;
+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;
+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
+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;
+ALTER FOREIGN TABLE ft1 SET WITHOUT OIDS;
+ALTER FOREIGN TABLE ft1 ENABLE RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 ENABLE ALWAYS RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 ENABLE REPLICA RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 DISABLE RULE ft1_insert_rule;
+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 opt1, SET opt2 'VAL2', ADD opt3 'val3');
+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 +343,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 +448,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;
index 479bf8542a7e805937e6ccb7c7294ffd195224db..94c6426e3c66ccc199bae060a0d94520388447d3 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.