]> granicus.if.org Git - postgresql/commitdiff
Basic foreign table support.
authorRobert Haas <rhaas@postgresql.org>
Sun, 2 Jan 2011 04:48:11 +0000 (23:48 -0500)
committerRobert Haas <rhaas@postgresql.org>
Sun, 2 Jan 2011 04:48:11 +0000 (23:48 -0500)
Foreign tables are a core component of SQL/MED.  This commit does
not provide a working SQL/MED infrastructure, because foreign tables
cannot yet be queried.  Support for foreign table scans will need to
be added in a future patch.  However, this patch creates the necessary
system catalog structure, syntax support, and support for ancillary
operations such as COMMENT and SECURITY LABEL.

Shigeru Hanada, heavily revised by Robert Haas

69 files changed:
contrib/pageinspect/rawpage.c
contrib/pgstattuple/pgstattuple.c
doc/src/sgml/catalogs.sgml
doc/src/sgml/information_schema.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/alter_default_privileges.sgml
doc/src/sgml/ref/alter_foreign_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/ref/security_label.sgml
doc/src/sgml/reference.sgml
src/backend/access/common/reloptions.c
src/backend/access/heap/heapam.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/catalog/system_views.sql
src/backend/commands/alter.c
src/backend/commands/analyze.c
src/backend/commands/comment.c
src/backend/commands/copy.c
src/backend/commands/foreigncmds.c
src/backend/commands/seclabel.c
src/backend/commands/tablecmds.c
src/backend/commands/vacuum.c
src/backend/executor/execMain.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/util/plancat.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/backend/tcop/utility.c
src/backend/utils/adt/acl.c
src/backend/utils/cache/syscache.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/bin/psql/tab-complete.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/catalog/indexing.h
src/include/catalog/pg_class.h
src/include/catalog/pg_foreign_table.h [new file with mode: 0644]
src/include/commands/defrem.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/rules.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 e4e933dfaf77acead761c056b2b7f13d358f6843..2607576c378d3f6bbcab7c20abe8e258ffa3108c 100644 (file)
@@ -119,6 +119,11 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 errmsg("cannot get raw page from composite type \"%s\"",
                                                RelationGetRelationName(rel))));
+       if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("cannot get raw page from foreign table \"%s\"",
+                                               RelationGetRelationName(rel))));
 
        /*
         * Reject attempts to read non-local temporary relations; we would be
index 3a5d9c27b442b88961d8c5fb2fb4af3e60204d38..e5ddd87091036961e3e7a843288bdb0c024f72f4 100644 (file)
@@ -242,6 +242,9 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
                case RELKIND_COMPOSITE_TYPE:
                        err = "composite type";
                        break;
+               case RELKIND_FOREIGN_TABLE:
+                       err = "foreign table";
+                       break;
                default:
                        err = "unknown";
                        break;
index 0eeb499207e4b1bc1a6530dd957ce6b5560401fc..67ba3400da2c12a61f79ff87d0bcd7623cc4229b 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</> = foreign 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>ftoptions</structfield></entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>
+       Foreign table options, as <quote>keyword=value</> strings.
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+
  <sect1 id="catalog-pg-index">
   <title><structname>pg_index</structname></title>
 
index 9d30949aa77dc6ea5a0c03221765c3c9a94add5a..962b85b8e4bfaf444af51ca77bf5e7d6ebd5f5c4 100644 (file)
@@ -2384,6 +2384,132 @@ ORDER BY c.ordinal_position;
   </table>
  </sect1>
 
+ <sect1 id="infoschema-foreign-table-options">
+  <title><literal>foreign_table_options</literal></title>
+
+  <para>
+   The view <literal>foreign_table_options</literal> contains all the
+   options defined for foreign tables in the current database.  Only
+   those foreign tables are shown that the current user has access to
+   (by way of being the owner or having some privilege).
+  </para>
+
+  <table>
+   <title><literal>foreign_table_options</literal> Columns</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Data Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><literal>foreign_table_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that contains the foreign table (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_table_schema</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the schema that contains the foreign table</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_table_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign table</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that the foreign server is defined in (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign server</entry>
+     </row>
+
+     <row>
+      <entry><literal>option_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of an option</entry>
+     </row>
+
+     <row>
+      <entry><literal>option_value</literal></entry>
+      <entry><type>character_data</type></entry>
+      <entry>Value of the option</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
+ <sect1 id="infoschema-foreign-tables">
+  <title><literal>foreign_tables</literal></title>
+
+  <para>
+   The view <literal>foreign_tables</literal> contains all foreign
+   tables defined in the current database.  Only those foreign
+   tables are shown that the current user has access to (by way of
+   being the owner or having some privilege).
+  </para>
+
+  <table>
+   <title><literal>foreign_tables</literal> Columns</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Data Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><literal>foreign_table_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that the foreign table is defined in (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_table_schema</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the schema that contains the foreign table</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_table_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign table</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_catalog</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the database that the foreign server is defined in (always the current database)</entry>
+     </row>
+
+     <row>
+      <entry><literal>foreign_server_name</literal></entry>
+      <entry><type>sql_identifier</type></entry>
+      <entry>Name of the foreign server</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
  <sect1 id="infoschema-key-column-usage">
   <title><literal>key_column_usage</literal></title>
 
@@ -4730,8 +4856,9 @@ ORDER BY c.ordinal_position;
       <entry>
        Type of the table: <literal>BASE TABLE</literal> for a
        persistent base table (the normal table type),
-       <literal>VIEW</literal> for a view, or <literal>LOCAL
-       TEMPORARY</literal> for a temporary table
+       <literal>VIEW</literal> for a view, <literal>FOREIGN TABLE</literal>
+       for a foreign table, or
+       <literal>LOCAL TEMPORARY</literal> for a temporary table
       </entry>
      </row>
 
index a352a431419b8b36722f41a85a716d4f3a21a449..f40fa9dd8b26267d7910d090e0360d547b01b8c5 100644 (file)
@@ -12,6 +12,7 @@ Complete list of usable sgml source files in this directory.
 <!entity alterDefaultPrivileges system "alter_default_privileges.sgml">
 <!entity alterDomain        system "alter_domain.sgml">
 <!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml">
+<!entity alterForeignTable  system "alter_foreign_table.sgml">
 <!entity alterFunction      system "alter_function.sgml">
 <!entity alterGroup         system "alter_group.sgml">
 <!entity alterIndex         system "alter_index.sgml">
@@ -50,6 +51,7 @@ Complete list of usable sgml source files in this directory.
 <!entity createDatabase     system "create_database.sgml">
 <!entity createDomain       system "create_domain.sgml">
 <!entity createForeignDataWrapper system "create_foreign_data_wrapper.sgml">
+<!entity createForeignTable system "create_foreign_table.sgml">
 <!entity createFunction     system "create_function.sgml">
 <!entity createGroup        system "create_group.sgml">
 <!entity createIndex        system "create_index.sgml">
@@ -85,6 +87,7 @@ Complete list of usable sgml source files in this directory.
 <!entity dropDatabase       system "drop_database.sgml">
 <!entity dropDomain         system "drop_domain.sgml">
 <!entity dropForeignDataWrapper system "drop_foreign_data_wrapper.sgml">
+<!entity dropForeignTable   system "drop_foreign_table.sgml">
 <!entity dropFunction       system "drop_function.sgml">
 <!entity dropGroup          system "drop_group.sgml">
 <!entity dropIndex          system "drop_index.sgml">
index c27466fbef9aeacb952b5270d0b13f0c11800299..cc4f3636a25c983955f61c8c24da66d6ab0b42a0 100644 (file)
@@ -71,8 +71,8 @@ REVOKE [ GRANT OPTION FOR ]
    <command>ALTER DEFAULT PRIVILEGES</> allows you to set the privileges
    that will be applied to objects created in the future.  (It does not
    affect privileges assigned to already-existing objects.)  Currently,
-   only the privileges for tables (including views), sequences, and
-   functions can be altered.
+   only the privileges for tables (including views and foreign tables),
+   sequences, and functions can be altered.
   </para>
 
   <para>
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..9d14b19
--- /dev/null
@@ -0,0 +1,315 @@
+<!--
+doc/src/sgml/rel/alter_foreign_table.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERFOREIGNTABLE">
+ <refmeta>
+  <refentrytitle>ALTER FOREIGN TABLE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER FOREIGN TABLE</refname>
+  <refpurpose>change the definition of a foreign table</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterforeigntable">
+  <primary>ALTER FOREIGN TABLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
+    <replaceable class="PARAMETER">action</replaceable> [, ... ]
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
+    RENAME [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> TO <replaceable class="PARAMETER">new_column</replaceable>
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
+    RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
+    SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+
+    ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">type</replaceable>
+    DROP [ COLUMN ] [ IF EXISTS ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">type</replaceable>
+    ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
+    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 foreign table.  There are several subforms:
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>ADD COLUMN</literal></term>
+    <listitem>
+     <para>
+      This form adds a new column to the foreign table, using the same syntax as
+      <xref linkend="SQL-CREATEFOREIGNTABLE">.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>DROP COLUMN [ IF EXISTS ]</literal></term>
+    <listitem>
+     <para>
+      This form drops a column from a foreign table.
+      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.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET</literal>/<literal>DROP NOT NULL</literal></term>
+    <listitem>
+     <para>
+      Mark a column as allowing, or not allowing, null values.
+     </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.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET SCHEMA</literal></term>
+    <listitem>
+     <para>
+      This form moves the foreign table into another schema.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
+    <listitem>
+     <para>
+      Change options for the foreign table or the column of the foreign table.
+      <literal>ADD</>, <literal>SET</>, and <literal>DROP</>
+      specify the action to be performed.  <literal>ADD</> is assumed
+      if no operation is explicitly specified.  Option names must be
+      unique; names and values are also validated using the foreign
+      data wrapper library.
+     </para>
+    </listitem>
+   </varlistentry>
+
+  </variablelist>
+  </para>
+
+  <para>
+   All the actions except <literal>RENAME</literal> and <literal>SET SCHEMA</>
+   can be combined into
+   a list of multiple alterations to apply in parallel.  For example, it
+   is possible to add several columns and/or alter the type of several
+   columns in a single command.
+  </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 foreign table to
+        alter.
+       </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><literal>CASCADE</literal></term>
+      <listitem>
+       <para>
+        Automatically drop objects that depend on the dropped column
+        (for example, views referencing the column).
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>RESTRICT</literal></term>
+      <listitem>
+       <para>
+        Refuse to drop the column if there are any dependent
+        objects. This is the default behavior.
+       </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 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</>.  It is the
+    user's responsibility to ensure that the table definition matches the
+    remote side.
+   </para>
+
+   <para>
+    Refer to <xref linkend="sql-createforeigntable"> for a further description of valid
+    parameters.
+   </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   To mark a column as not-null:
+<programlisting>
+ALTER FOREIGN TABLE distributors ALTER COLUMN street SET NOT NULL;
+</programlisting>
+  </para>
+
+  <para>
+   To change 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 d81fd726414d8ef6bc36f580941961379b1762a4..f1a1605df3c48a9039fa94970998de63a0880e77 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> |
@@ -96,9 +97,9 @@ COMMENT ON
     <listitem>
      <para>
       The name of the object to be commented.  Names of tables,
-      aggregates, domains, functions, indexes, operators, operator classes,
-      operator families, sequences, text search objects, types, and views can
-      be schema-qualified.
+      aggregates, domains, foreign tables, functions, indexes, operators,
+      operator classes, operator families, sequences, text search objects,
+      types, and views can be schema-qualified.
      </para>
     </listitem>
    </varlistentry>
@@ -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_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..ac2e139
--- /dev/null
@@ -0,0 +1,188 @@
+<!-- doc/src/sgml/ref/create_foreign_table.sgml -->
+
+<refentry id="SQL-CREATEFOREIGNTABLE">
+ <refmeta>
+  <refentrytitle>CREATE FOREIGN TABLE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE FOREIGN TABLE</refname>
+  <refpurpose>define a new foreign table</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-createforeigntable">
+  <primary>CREATE FOREIGN TABLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [
+  { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ NULL | NOT NULL ] }
+    [, ... ]
+] )
+  SERVER <replaceable class="parameter">server_name</replaceable>
+[ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
+
+</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>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+
+   <varlistentry>
+    <term><literal>IF NOT EXISTS</></term>
+    <listitem>
+     <para>
+      Do not throw an error if a relation with the same name already exists.
+      A notice is issued in this case.  Note that there is no guarantee that
+      the existing relation is anything like the one that would have been
+      created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">table_name</replaceable></term>
+    <listitem>
+     <para>
+      The name (optionally schema-qualified) of the table to be created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">column_name</replaceable></term>
+    <listitem>
+     <para>
+      The name of a column to be created in the new table.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">data_type</replaceable></term>
+    <listitem>
+     <para>
+      The data type of the column. This can include array
+      specifiers. For more information on the data types supported by
+      <productname>PostgreSQL</productname>, refer to <xref
+      linkend="datatype">.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>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>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> = '<replaceable class="PARAMETER">value</replaceable>' [, ...] )</literal></term>
+    <listitem>
+     <para>
+      Options to be associated with the new foreign table.
+      The allowed option names and values are specific to each foreign
+      data wrapper and are validated using the foreign-data wrapper
+      library. Option names must be unique. 
+     </para>
+    </listitem>
+   </varlistentry>
+
+  </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>
+
+ </refsect1>
+
+ <refsect1 id="SQL-CREATEFOREIGNTABLE-compatibility">
+  <title id="SQL-CREATEFOREIGNTABLE-compatibility-title">Compatibility</title>
+
+  <para>
+   The <command>CREATE FOREIGN TABLE</command> command largely conforms to the
+   <acronym>SQL</acronym> standard; however, much as with
+   <link linkend="sql-createtable"><command>CREATE TABLE</></link>,
+   <literal>NULL</> constraints and zero-column foreign tables are permitted.
+  </para>
+
+ </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-createserver"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
index 7f94d24865d7c019253f33e93f433fcf6b65bc3a..f36b616db0ebe989709e7a968f82a6aa64264a98 100644 (file)
@@ -45,7 +45,7 @@ CREATE [ TEMPORARY | TEMP ] SEQUENCE <replaceable class="parameter">name</replac
    Temporary sequences exist in a special schema, so a schema name cannot be
    given when creating a temporary sequence.
    The sequence name must be distinct from the name of any other sequence,
-   table, index, or view in the same schema.
+   table, index, view, or foreign table in the same schema.
   </para>
 
   <para>
index efb4b1aca18f68030ad7f52fcc2810f857a0398a..e2967bf0ca0df69f13f6c65559021d287152cf7c 100644 (file)
@@ -97,8 +97,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
    schema.  Otherwise it is created in the current schema.  Temporary
    tables exist in a special schema, so a schema name cannot be given
    when creating a temporary table.  The name of the table must be
-   distinct from the name of any other table, sequence, index, or view
-   in the same schema.
+   distinct from the name of any other table, sequence, index, view,
+   or foreign table in the same schema.
   </para>
 
   <para>
index 6676383ab078fbe095766da61b0e8a92e3c2d86a..dd1550781115ee9770c9e238a52faab97e6749d7 100644 (file)
@@ -50,7 +50,7 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
    schema.  Otherwise it is created in the current schema.  Temporary
    views exist in a special schema, so a schema name cannot be given
    when creating a temporary view. The name of the view must be
-   distinct from the name of any other view, table, sequence, or index
+   distinct from the name of any other view, table, sequence, index or foreign table
    in the same schema.
   </para>
  </refsect1>
diff --git a/doc/src/sgml/ref/drop_foreign_table.sgml b/doc/src/sgml/ref/drop_foreign_table.sgml
new file mode 100644 (file)
index 0000000..71f2646
--- /dev/null
@@ -0,0 +1,112 @@
+<!-- doc/src/sggml/ref/drop_foreign_table.sgml -->
+
+<refentry id="SQL-DROPFOREIGNTABLE">
+ <refmeta>
+  <refentrytitle>DROP FOREIGN TABLE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP FOREIGN TABLE</refname>
+  <refpurpose>remove a foreign table</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-dropforeigntable">
+  <primary>DROP FOREIGN TABLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP FOREIGN TABLE</command> removes a foreign table.
+   Only the owner of a foreign table can remove it.
+  </para>
+ </refsect1>
+  
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the foreign 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 the foreign table to drop.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CASCADE</literal></term>
+    <listitem>
+     <para>
+      Automatically drop objects that depend on the foreign table (such as
+      views).
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+    <listitem>
+     <para>
+      Refuse to drop the foreign 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 b291a257ea70da71a864741137b681b54a1259e8..de4968c5a038be6a3626d69df4cd7275e2671cca 100644 (file)
@@ -408,9 +408,9 @@ PostgreSQL documentation
       <term><option>--table=<replaceable class="parameter">table</replaceable></option></term>
       <listitem>
        <para>
-        Dump only tables (or views or sequences) matching <replaceable
-        class="parameter">table</replaceable>.  Multiple tables can be
-        selected by writing multiple <option>-t</> switches.  Also, the
+        Dump only tables (or views or sequences or foreign tables) matching
+        <replaceable class="parameter">table</replaceable>.  Multiple tables
+        can be selected by writing multiple <option>-t</> switches.  Also, the
         <replaceable class="parameter">table</replaceable> parameter is
         interpreted as a pattern according to the same rules used by
         <application>psql</>'s <literal>\d</> commands (see <xref
index d44fc56b37e1c11cfcc659a27812cc35b7a9a160..0c658d1f5b3f9ba08acc78b236acb12f24926e49 100644 (file)
@@ -867,12 +867,14 @@ testdb=&gt;
 
         <listitem>
         <para>
-        For each relation (table, view, index, or sequence) matching the
+        For each relation (table, view, index, sequence or foreign table)
+        matching the
         <replaceable class="parameter">pattern</replaceable>, show all
         columns, their types, the tablespace (if not the default) and any
         special attributes such as <literal>NOT NULL</literal> or defaults.
         Associated indexes, constraints, rules, and triggers are
-        also shown.
+        also shown.  For foreign tables, the associated foreign
+        server is shown as well.
         (<quote>Matching the pattern</> is defined in
         <xref linkend="APP-PSQL-patterns" endterm="APP-PSQL-patterns-title">
         below.)
@@ -882,7 +884,8 @@ testdb=&gt;
         The command form <literal>\d+</literal> is identical, except that
         more information is displayed: any comments associated with the
         columns of the table are shown, as is the presence of OIDs in the
-        table, and the view definition if the relation is a view.
+        table, the view definition if the relation is a view, and the generic
+        options if the relation is a foreign table.
         </para>
 
         <para>
@@ -895,9 +898,9 @@ testdb=&gt;
         <para>
         If <command>\d</command> is used without a
         <replaceable class="parameter">pattern</replaceable> argument, it is
-        equivalent to <command>\dtvs</command> which will show a list of
-        all visible tables, views, and sequences. This is purely a convenience
-        measure.
+        equivalent to <command>\dtvsE</command> which will show a list of
+        all visible tables, views, sequences and foreign tables.
+        This is purely a convenience measure.
         </para>
         </note>
         </listitem>
@@ -1034,6 +1037,20 @@ testdb=&gt;
       </varlistentry>
 
 
+      <varlistentry>
+        <term><literal>\det[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <listitem>
+        <para>
+        Lists foreign tables (mnemonic: <quote>external tables</quote>).
+        If <replaceable class="parameter">pattern</replaceable> is
+        specified, only entries whose table name or schema name matches
+        the pattern are listed.  If the form <literal>\det+</literal>
+        is used, generic options are also displayed.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\des[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
@@ -1197,13 +1214,15 @@ testdb=&gt;
         <term><literal>\ds[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <term><literal>\dt[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <term><literal>\dv[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+        <term><literal>\dE[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
 
         <listitem>
         <para>
         In this group of commands, the letters
         <literal>i</literal>, <literal>s</literal>,
-        <literal>t</literal>, and <literal>v</literal>
-        stand for index, sequence, table, and view, respectively.
+        <literal>t</literal>, <literal>v</literal>, and <literal>E</literal>
+        stand for index, sequence, table, view, and foreign table,
+        respectively.
         You can specify any or all of
         these letters, in any order, to obtain a listing of objects
         of these types.  For example, <literal>\dit</> lists indexes
index ca9d63ee772130b96438bdbd4a9b1b84bbab7060..74169d04e3e346a404c2948885286d8af276a1f1 100644 (file)
@@ -27,6 +27,7 @@ SECURITY LABEL [ FOR <replaceable class="PARAMETER">provider</replaceable> ] ON
   COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
   AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</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> [, ...] ] ) |
   LARGE OBJECT <replaceable class="PARAMETER">large_object_oid</replaceable> |
   [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">object_name</replaceable> |
@@ -82,8 +83,8 @@ SECURITY LABEL [ FOR <replaceable class="PARAMETER">provider</replaceable> ] ON
     <listitem>
      <para>
       The name of the object to be commented.  Names of tables,
-      aggregates, domains, functions, sequences, types, and views can
-      be schema-qualified.
+      aggregates, domains, foreign tables, functions, sequences, types, and
+      views can be schema-qualified.
      </para>
     </listitem>
    </varlistentry>
index 13de002792538595d0a7c23a9b79f19144dc328f..84babf61c00c70d5b6a5f19c65fd9956dc1cb821 100644 (file)
@@ -40,6 +40,7 @@
    &alterDefaultPrivileges;
    &alterDomain;
    &alterForeignDataWrapper;
+   &alterForeignTable;
    &alterFunction;
    &alterGroup;
    &alterIndex;
@@ -78,6 +79,7 @@
    &createDatabase;
    &createDomain;
    &createForeignDataWrapper;
+   &createForeignTable;
    &createFunction;
    &createGroup;
    &createIndex;
    &dropDatabase;
    &dropDomain;
    &dropForeignDataWrapper;
+   &dropForeignTable;
    &dropFunction;
    &dropGroup;
    &dropIndex;
index eb6ccc6582cbb6e9cf3a3b2253db035ee9fa1fa5..d2e0531e3548c5811ea898c6c5e8dee7987f0837 100644 (file)
@@ -782,6 +782,9 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
                case RELKIND_INDEX:
                        options = index_reloptions(amoptions, datum, false);
                        break;
+               case RELKIND_FOREIGN_TABLE:
+                       options = NULL;
+                       break;
                default:
                        Assert(false);          /* can't get here */
                        options = NULL;         /* keep compiler quiet */
@@ -1174,7 +1177,7 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
                case RELKIND_RELATION:
                        return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
                default:
-                       /* sequences, composite types and views are not supported */
+                       /* other relkinds are not supported */
                        return NULL;
        }
 }
index ac744e4187eafd8c4fa152f22689fc4012f60f8b..25d9fdea3a0a9eafd1b7955810f1c8bfe74a75d6 100644 (file)
@@ -1060,7 +1060,8 @@ relation_close(Relation relation, LOCKMODE lockmode)
  *
  *             This is essentially relation_open plus check that the relation
  *             is not an index nor a composite type.  (The caller should also
- *             check that it's not a view before assuming it has storage.)
+ *             check that it's not a view or foreign table before assuming it has
+ *      storage.)
  * ----------------
  */
 Relation
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 01105aae1e6aadb71f6b54fe00c768ca2d99d853..e07db507c09b22e5a572197220f2c294c46b3988 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 regular tables, views and foreign tables */
                                objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
                                objects = list_concat(objects, objs);
                                objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
                                objects = list_concat(objects, objs);
+                               objs = getRelationsInNamespace(namespaceId, RELKIND_FOREIGN_TABLE);
+                               objects = list_concat(objects, objs);
                                break;
                        case ACL_OBJECT_SEQUENCE:
                                objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
@@ -1689,11 +1700,21 @@ ExecGrant_Relation(InternalGrant *istmt)
                                         errmsg("\"%s\" is not a sequence",
                                                        NameStr(pg_class_tuple->relname))));
 
-               /* Adjust the default permissions based on whether it is a sequence */
+               /* 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 object type */
                if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
                {
                        if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
                                this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+                       else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE)
+                               this_privileges = ACL_ALL_RIGHTS_FOREIGN_TABLE;
                        else
                                this_privileges = ACL_ALL_RIGHTS_RELATION;
                }
@@ -1729,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))
@@ -1781,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;
@@ -1812,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.
@@ -1825,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);
 
@@ -1909,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 as sequences. */
+                               ereport(WARNING,
+                                       (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+                                        errmsg("foreign table \"%s\" only supports SELECT column privileges",
+                                                       NameStr(pg_class_tuple->relname))));
+                               this_privileges &= (AclMode) ACL_SELECT;
+                       }
 
                        expand_col_privileges(col_privs->cols, relOid,
                                                                  this_privileges,
@@ -3080,7 +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] =
@@ -3120,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"),
 };
 
 
@@ -3410,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 7627c7dd20b0301212feb31033b34dce91cca326..ec8eb74954a9cc0ec3623dc42dfed462dc1a3533 100644 (file)
@@ -36,6 +36,7 @@
 #include "catalog/pg_depend.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_namespace.h"
@@ -152,6 +153,7 @@ static const Oid object_classes[MAX_OCLASS] = {
        ForeignDataWrapperRelationId,           /* OCLASS_FDW */
        ForeignServerRelationId,        /* OCLASS_FOREIGN_SERVER */
        UserMappingRelationId,          /* OCLASS_USER_MAPPING */
+       ForeignTableRelationId,         /* OCLASS_FOREIGN_TABLE */
        DefaultAclRelationId            /* OCLASS_DEFACL */
 };
 
@@ -2072,6 +2074,10 @@ getObjectClass(const ObjectAddress *object)
                case UserMappingRelationId:
                        return OCLASS_USER_MAPPING;
 
+               case ForeignTableRelationId:
+                       Assert(object->objectSubId == 0);
+                       return OCLASS_FOREIGN_TABLE;
+
                case DefaultAclRelationId:
                        return OCLASS_DEFACL;
        }
@@ -2754,6 +2760,10 @@ getRelationDescription(StringInfo buffer, Oid relid)
                        appendStringInfo(buffer, _("composite type %s"),
                                                         relname);
                        break;
+               case RELKIND_FOREIGN_TABLE:
+                       appendStringInfo(buffer, _("foreign table %s"),
+                                                        relname);
+                       break;
                default:
                        /* shouldn't get here */
                        appendStringInfo(buffer, _("relation %s"),
index 7cdff403d8d6169eb833f167974d8a72bd1cf022..4c55db7e3ccfe283342998e676d4c6542665de67 100644 (file)
@@ -43,6 +43,7 @@
 #include "catalog/objectaccess.h"
 #include "catalog/pg_attrdef.h"
 #include "catalog/pg_constraint.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_statistic.h"
@@ -269,6 +270,7 @@ heap_create(const char *relname,
        {
                case RELKIND_VIEW:
                case RELKIND_COMPOSITE_TYPE:
+               case RELKIND_FOREIGN_TABLE:
                        create_storage = false;
 
                        /*
@@ -987,7 +989,8 @@ heap_create_with_catalog(const char *relname,
                /* Use binary-upgrade overrides if applicable */
                if (OidIsValid(binary_upgrade_next_heap_relfilenode) &&
                        (relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE ||
-                        relkind == RELKIND_VIEW || relkind == RELKIND_COMPOSITE_TYPE))
+                        relkind == RELKIND_VIEW || relkind == RELKIND_COMPOSITE_TYPE ||
+                        relkind == RELKIND_FOREIGN_TABLE))
                {
                        relid = binary_upgrade_next_heap_relfilenode;
                        binary_upgrade_next_heap_relfilenode = InvalidOid;
@@ -1012,6 +1015,7 @@ heap_create_with_catalog(const char *relname,
                {
                        case RELKIND_RELATION:
                        case RELKIND_VIEW:
+                       case RELKIND_FOREIGN_TABLE:
                                relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid,
                                                                                          relnamespace);
                                break;
@@ -1049,10 +1053,12 @@ 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 ||
+                                                         relkind == RELKIND_FOREIGN_TABLE ||
                                                          relkind == RELKIND_COMPOSITE_TYPE))
                new_array_oid = AssignTypeArrayOid();
 
@@ -1589,11 +1595,32 @@ 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 = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
+               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 c4f9d4d5c437afba72b1914270e0cdc9659837ba..090c10c3220e6f8b41f60ad83135830206417de4 100644 (file)
@@ -465,7 +465,7 @@ CREATE VIEW column_domain_usage AS
           AND a.attrelid = c.oid
           AND a.atttypid = t.oid
           AND t.typtype = 'd'
-          AND c.relkind IN ('r', 'v')
+          AND c.relkind IN ('r', 'v', 'f')
           AND a.attnum > 0
           AND NOT a.attisdropped
           AND pg_has_role(t.typowner, 'USAGE');
@@ -504,7 +504,7 @@ CREATE VIEW column_privileges AS
                   pr_c.relowner
            FROM (SELECT oid, relname, relnamespace, relowner, (aclexplode(relacl)).*
                  FROM pg_class
-                 WHERE relkind IN ('r', 'v')
+                 WHERE relkind IN ('r', 'v', 'f')
                 ) pr_c (oid, relname, relnamespace, relowner, grantor, grantee, prtype, grantable),
                 pg_attribute a
            WHERE a.attrelid = pr_c.oid
@@ -526,7 +526,7 @@ CREATE VIEW column_privileges AS
                 ) pr_a (attrelid, attname, grantor, grantee, prtype, grantable),
                 pg_class c
            WHERE pr_a.attrelid = c.oid
-                 AND relkind IN ('r','v')
+                 AND relkind IN ('r', 'v', 'f')
          ) x,
          pg_namespace nc,
          pg_authid u_grantor,
@@ -569,7 +569,7 @@ CREATE VIEW column_udt_usage AS
     WHERE a.attrelid = c.oid
           AND a.atttypid = t.oid
           AND nc.oid = c.relnamespace
-          AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v')
+          AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v', 'f')
           AND pg_has_role(coalesce(bt.typowner, t.typowner), 'USAGE');
 
 GRANT SELECT ON column_udt_usage TO PUBLIC;
@@ -692,7 +692,7 @@ CREATE VIEW columns AS
           AND nc.oid = c.relnamespace
           AND (NOT pg_is_other_temp_schema(nc.oid))
 
-          AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v')
+          AND a.attnum > 0 AND NOT a.attisdropped AND c.relkind in ('r', 'v', 'f')
 
           AND (pg_has_role(c.relowner, 'USAGE')
                OR has_column_privilege(c.oid, a.attnum,
@@ -1793,6 +1793,7 @@ CREATE VIEW tables AS
              CASE WHEN nc.oid = pg_my_temp_schema() THEN 'LOCAL TEMPORARY'
                   WHEN c.relkind = 'r' THEN 'BASE TABLE'
                   WHEN c.relkind = 'v' THEN 'VIEW'
+                  WHEN c.relkind = 'f' THEN 'FOREIGN TABLE'
                   ELSE null END
              AS character_data) AS table_type,
 
@@ -1817,7 +1818,7 @@ CREATE VIEW tables AS
     FROM pg_namespace nc JOIN pg_class c ON (nc.oid = c.relnamespace)
            LEFT JOIN (pg_type t JOIN pg_namespace nt ON (t.typnamespace = nt.oid)) ON (c.reloftype = t.oid)
 
-    WHERE c.relkind IN ('r', 'v')
+    WHERE c.relkind IN ('r', 'v', 'f')
           AND (NOT pg_is_other_temp_schema(nc.oid))
           AND (pg_has_role(c.relowner, 'USAGE')
                OR has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
@@ -2129,7 +2130,7 @@ CREATE VIEW view_column_usage AS
           AND dt.refclassid = 'pg_catalog.pg_class'::regclass
           AND dt.refobjid = t.oid
           AND t.relnamespace = nt.oid
-          AND t.relkind IN ('r', 'v')
+          AND t.relkind IN ('r', 'v', 'f')
           AND t.oid = a.attrelid
           AND dt.refobjsubid = a.attnum
           AND pg_has_role(t.relowner, 'USAGE');
@@ -2199,7 +2200,7 @@ CREATE VIEW view_table_usage AS
           AND dt.refclassid = 'pg_catalog.pg_class'::regclass
           AND dt.refobjid = t.oid
           AND t.relnamespace = nt.oid
-          AND t.relkind IN ('r', 'v')
+          AND t.relkind IN ('r', 'v', 'f')
           AND pg_has_role(t.relowner, 'USAGE');
 
 GRANT SELECT ON view_table_usage TO PUBLIC;
@@ -2344,7 +2345,7 @@ CREATE VIEW element_types AS
                   'TABLE'::text, a.attnum, a.atttypid
            FROM pg_class c, pg_attribute a
            WHERE c.oid = a.attrelid
-                 AND c.relkind IN ('r', 'v')
+                 AND c.relkind IN ('r', 'v', 'f')
                  AND attnum > 0 AND NOT attisdropped
 
            UNION ALL
@@ -2481,6 +2482,60 @@ CREATE VIEW foreign_servers AS
 GRANT SELECT ON foreign_servers TO PUBLIC;
 
 
+/* Base view for foreign tables */
+CREATE VIEW _pg_foreign_tables AS
+    SELECT
+           CAST(current_database() AS sql_identifier) AS foreign_table_catalog,
+           n.nspname AS foreign_table_schema,
+           c.relname AS foreign_table_name,
+           t.ftoptions AS ftoptions,
+           CAST(current_database() AS sql_identifier) AS foreign_server_catalog,
+           CAST(srvname AS sql_identifier) AS foreign_server_name,
+           CAST(u.rolname AS sql_identifier) AS authorization_identifier
+    FROM pg_foreign_table t, pg_foreign_server s, pg_foreign_data_wrapper w,
+         pg_authid u, pg_namespace n, pg_class c
+    WHERE w.oid = s.srvfdw
+          AND u.oid = c.relowner
+          AND (pg_has_role(c.relowner, 'USAGE')
+               OR has_table_privilege(c.oid, 'SELECT')
+               OR has_any_column_privilege(c.oid, 'SELECT'))
+          AND n.oid = c.relnamespace
+          AND c.oid = t.ftrelid
+          AND c.relkind = 'f'
+          AND s.oid = t.ftserver;
+
+
+/*
+ * 24.8
+ * FOREIGN_TABLE_OPTIONS view
+ */
+CREATE VIEW foreign_table_options AS
+    SELECT foreign_table_catalog,
+           foreign_table_schema,
+           foreign_table_name,
+           CAST((pg_options_to_table(t.ftoptions)).option_name AS sql_identifier) AS option_name,
+           CAST((pg_options_to_table(t.ftoptions)).option_value AS character_data) AS option_value
+    FROM _pg_foreign_tables t;
+
+GRANT SELECT ON TABLE foreign_table_options TO PUBLIC;
+
+
+/*
+ * 24.9
+ * FOREIGN_TABLES view
+ */
+CREATE VIEW foreign_tables AS
+    SELECT foreign_table_catalog,
+           foreign_table_schema,
+           foreign_table_name,
+           foreign_server_catalog,
+           foreign_server_name
+    FROM _pg_foreign_tables;
+
+GRANT SELECT ON foreign_tables TO PUBLIC;
+
+
+
 /* Base view for user mappings */
 CREATE VIEW _pg_user_mappings AS
     SELECT um.oid,
index a81d1198e4fcd087324ed66eb99a76edf3912cf1..c4608f7f1732e2f9022a29c4c68180dac20c2183 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 8e25c9890af073819facdd3c6b313a332e52a432..ded046d9d066b5c815b7ab39fad6194b8e81df55 100644 (file)
@@ -167,8 +167,9 @@ CREATE VIEW pg_seclabels AS
 SELECT
        l.objoid, l.classoid, l.objsubid,
        CASE WHEN rel.relkind = 'r' THEN 'table'::text
-            WHEN rel.relkind = 'v' THEN 'view'::text
-            WHEN rel.relkind = 'S' THEN 'sequence'::text END AS objtype,
+                WHEN rel.relkind = 'v' THEN 'view'::text
+                WHEN rel.relkind = 'S' THEN 'sequence'::text
+                WHEN rel.relkind = 'f' THEN 'foreign table'::text END AS objtype,
        rel.relnamespace AS objnamespace,
        CASE WHEN pg_table_is_visible(rel.oid)
             THEN quote_ident(rel.relname)
index 5c93acb8b4ea2f8b1007c8aa30a37f2dfeb04710..6a9b21d01fe86bb02df0be4cecc24ff11405ddfd 100644 (file)
@@ -95,6 +95,7 @@ ExecRenameStmt(RenameStmt *stmt)
                case OBJECT_COLUMN:
                case OBJECT_ATTRIBUTE:
                case OBJECT_TRIGGER:
+               case OBJECT_FOREIGN_TABLE:
                        {
                                Oid                     relid;
 
@@ -108,6 +109,7 @@ ExecRenameStmt(RenameStmt *stmt)
                                        case OBJECT_SEQUENCE:
                                        case OBJECT_VIEW:
                                        case OBJECT_INDEX:
+                                       case OBJECT_FOREIGN_TABLE:
                                                {
                                                        /*
                                                         * RENAME TABLE requires that we (still) hold
@@ -206,6 +208,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
                case OBJECT_SEQUENCE:
                case OBJECT_TABLE:
                case OBJECT_VIEW:
+               case OBJECT_FOREIGN_TABLE:
                        CheckRelationOwnership(stmt->relation, true);
                        AlterTableNamespace(stmt->relation, stmt->newschema,
                                                                stmt->objectType, AccessExclusiveLock);
index bd6f9651310c833b81c0da9f7b5d2ecdd252456d..7bc5f111f4d8ea6abba49661aba61d25f5aa688d 100644 (file)
@@ -187,7 +187,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
                /* No need for a WARNING if we already complained during VACUUM */
                if (!(vacstmt->options & VACOPT_VACUUM))
                        ereport(WARNING,
-                                       (errmsg("skipping \"%s\" --- cannot analyze indexes, views, or special system tables",
+                                       (errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables",
                                                        RelationGetRelationName(onerel))));
                relation_close(onerel, ShareUpdateExclusiveLock);
                return;
index 2c4242f78fa77312f6646d721d32d58d75a1c1c1..2e8c4df9272dd2433fd1e6b53d930541497ccc20 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,18 +575,20 @@ CheckAttributeComment(Relation relation)
                                           RelationGetRelationName(relation));
 
        /*
-        * Allow comments only on columns of tables, views, and composite types
-        * (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
-        * versions, so dumping per-column comments could create reload failures.
+        * 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 versions, so dumping per-column comments could create reload
+        * failures.
         */
        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 2d06ea2030e9d65a76e730c2a6af91dfc2fd05b6..841bf220c766d390773b3afef4d5234d0d6e3dec 100644 (file)
@@ -1235,6 +1235,12 @@ DoCopyTo(CopyState cstate)
                                                 errmsg("cannot copy from view \"%s\"",
                                                                RelationGetRelationName(cstate->rel)),
                                                 errhint("Try the COPY (SELECT ...) TO variant.")));
+                       else if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                                errmsg("cannot copy from foreign table \"%s\"",
+                                                               RelationGetRelationName(cstate->rel)),
+                                                errhint("Try the COPY (SELECT ...) TO variant.")));
                        else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE)
                                ereport(ERROR,
                                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -1708,6 +1714,11 @@ CopyFrom(CopyState cstate)
                                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                         errmsg("cannot copy to view \"%s\"",
                                                        RelationGetRelationName(cstate->rel))));
+               else if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("cannot copy to foreign table \"%s\"",
+                                                       RelationGetRelationName(cstate->rel))));
                else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE)
                        ereport(ERROR,
                                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
index db5017bcac68cf3bdd55dc1d3fdd862d610c2842..2774fc52ba427f0ec8690932d510b07f089dffe7 100644 (file)
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "access/xact.h"
 #include "access/reloptions.h"
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
@@ -21,6 +22,7 @@
 #include "catalog/objectaccess.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_user_mapping.h"
@@ -88,7 +90,7 @@ optionListToArray(List *options)
  *
  * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING.
  */
-static Datum
+Datum
 transformGenericOptions(Oid catalogId,
                                                Datum oldOptions,
                                                List *options,
@@ -1158,3 +1160,92 @@ RemoveUserMappingById(Oid umId)
 
        heap_close(rel, RowExclusiveLock);
 }
+
+/*
+ * Create a foreign table
+ * call after DefineRelation().
+ */
+void
+CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
+{
+       Relation        ftrel;
+       Datum           ftoptions;
+       Datum           values[Natts_pg_foreign_table];
+       bool            nulls[Natts_pg_foreign_table];
+       HeapTuple       tuple;
+       Oid                     ftId;
+       AclResult       aclresult;
+       ObjectAddress myself;
+       ObjectAddress referenced;
+       Oid                     ownerId;
+       ForeignDataWrapper *fdw;
+       ForeignServer *server;
+
+       /*
+        * Advance command counter to ensure the pg_attribute tuple visible; the
+        * tuple might be updated to add constraints in previous step.
+        */
+       CommandCounterIncrement();
+
+       /*
+        * For now the owner cannot be specified on create. Use effective user ID.
+        */
+       ownerId = GetUserId();
+
+       /*
+        * Check that the foreign server exists and that we have USAGE on it. Also
+        * get the actual FDW for option validation etc.
+        */
+       server = GetForeignServerByName(stmt->servername, false);
+       aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
+
+       fdw = GetForeignDataWrapper(server->fdwid);
+
+       aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
+
+       /*
+        * Insert tuple into pg_foreign_table.
+        */
+       ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+       memset(values, 0, sizeof(values));
+       memset(nulls, false, sizeof(nulls));
+
+       values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
+       values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
+       /* Add table generic options */
+       ftoptions = transformGenericOptions(ForeignTableRelationId,
+                                                                               PointerGetDatum(NULL),
+                                                                               stmt->options,
+                                                                               fdw->fdwvalidator);
+
+       if (PointerIsValid(DatumGetPointer(ftoptions)))
+               values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
+       else
+               nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+       tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
+
+       /* pg_foreign_table don't have OID */
+       ftId = simple_heap_insert(ftrel, tuple);
+
+       CatalogUpdateIndexes(ftrel, tuple);
+
+       heap_freetuple(tuple);
+
+       /* Add pg_class dependency on the server */
+       myself.classId = RelationRelationId;
+       myself.objectId = relid;
+       myself.objectSubId = 0;
+
+       referenced.classId = ForeignServerRelationId;
+       referenced.objectId = server->serverid;
+       referenced.objectSubId = 0;
+       recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+       heap_close(ftrel, NoLock);
+}
index 670b9e3e51c9e3c9870f1b899bb3164c0f089904..b927e76abd29dda23215b5fbc77bb24b57b8d9da 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));
@@ -365,10 +366,11 @@ CheckAttributeSecLabel(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 576dfb6b0d196e83f367cbf798461ad4c743d3f2..f3bd565b986f4b0df9eeed93fc98c083f52a9800 100644 (file)
@@ -29,6 +29,7 @@
 #include "catalog/objectaccess.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_inherits_fn.h"
 #include "catalog/pg_namespace.h"
@@ -48,6 +49,7 @@
 #include "commands/trigger.h"
 #include "commands/typecmds.h"
 #include "executor/executor.h"
+#include "foreign/foreign.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -219,9 +221,21 @@ 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}
 };
 
+/* Alter table target-type flags for ATSimplePermissions */
+#define                ATT_TABLE                               0x0001
+#define                ATT_VIEW                                0x0002
+#define                ATT_INDEX                               0x0004
+#define                ATT_COMPOSITE_TYPE              0x0008
+#define                ATT_FOREIGN_TABLE               0x0010
 
 static void truncate_check_rel(Relation rel);
 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -264,8 +278,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 ATSimplePermissionsRelationOrIndex(Relation rel);
+static void ATSimplePermissions(Relation rel, int allowed_targets);
+static void ATWrongRelkindError(Relation rel, int allowed_targets);
 static void ATSimpleRecursion(List **wqueue, Relation rel,
                                  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
 static void ATOneLevelRecursion(List **wqueue, Relation rel,
@@ -338,6 +352,8 @@ static void ATExecEnableDisableRule(Relation rel, char *rulename,
 static void ATPrepAddInherit(Relation child_rel);
 static void ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
 static void ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
+static void ATExecGenericOptions(Relation rel, List *options);
+
 static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
                                   ForkNumber forkNum, char relpersistence);
 static const char *storage_name(char c);
@@ -396,6 +412,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                 errmsg("ON COMMIT can only be used on temporary tables")));
+       if (stmt->constraints != NIL && relkind == RELKIND_FOREIGN_TABLE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("constraints on foreign tables are not supported")));
 
        /*
         * Security check: disallow creating temp tables from security-restricted
@@ -519,6 +539,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
                {
                        RawColumnDefault *rawEnt;
 
+                       if (relkind == RELKIND_FOREIGN_TABLE)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("default values on foreign tables are not supported")));
+
                        Assert(colDef->cooked_default == NULL);
 
                        rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
@@ -675,7 +700,8 @@ DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
 
 /*
  * RemoveRelations
- *             Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW
+ *             Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
+ *      DROP FOREIGN TABLE
  */
 void
 RemoveRelations(DropStmt *drop)
@@ -709,6 +735,10 @@ RemoveRelations(DropStmt *drop)
                        relkind = RELKIND_VIEW;
                        break;
 
+               case OBJECT_FOREIGN_TABLE:
+                       relkind = RELKIND_FOREIGN_TABLE;
+                       break;
+
                default:
                        elog(ERROR, "unrecognized drop object type: %d",
                                 (int) drop->removeType);
@@ -1991,10 +2021,11 @@ renameatt_internal(Oid myrelid,
        if (relkind != RELKIND_RELATION &&
                relkind != RELKIND_VIEW &&
                relkind != RELKIND_COMPOSITE_TYPE &&
-               relkind != RELKIND_INDEX)
+               relkind != RELKIND_INDEX &&
+               relkind != RELKIND_FOREIGN_TABLE)
                ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                          errmsg("\"%s\" is not a table, view, composite type or index",
+                          errmsg("\"%s\" is not a table, view, composite type, index or foreign table",
                                          RelationGetRelationName(targetrelation))));
 
        /*
@@ -2154,7 +2185,7 @@ renameatt(Oid myrelid, RenameStmt *stmt)
 
 
 /*
- * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW RENAME
+ * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE RENAME
  *
  * Caller has already done permissions checks.
  */
@@ -2176,7 +2207,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype)
 
        /*
         * For compatibility with prior releases, we don't complain if ALTER TABLE
-        * or ALTER INDEX is used to rename a sequence or view.
+        * or ALTER INDEX is used to rename some other type of relation.  But
+        * ALTER SEQUENCE/VIEW/FOREIGN TABLE are only to be used with relations of
+        * that type.
         */
        if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
                ereport(ERROR,
@@ -2190,6 +2223,13 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype)
                                 errmsg("\"%s\" is not a view",
                                                RelationGetRelationName(targetrelation))));
 
+       if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("\"%s\" is not a foreign table",
+                                               RelationGetRelationName(targetrelation)),
+                                errhint("Use ALTER FOREIGN TABLE instead.")));
+
        /*
         * Don't allow ALTER TABLE on composite types. We want people to use ALTER
         * TYPE for that.
@@ -2402,7 +2442,8 @@ AlterTable(AlterTableStmt *stmt)
                         * For mostly-historical reasons, we allow ALTER TABLE to apply to
                         * almost all relation types.
                         */
-                       if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+                       if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE
+                               || rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
                                ereport(ERROR,
                                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                                 errmsg("\"%s\" is not a table",
@@ -2441,6 +2482,14 @@ AlterTable(AlterTableStmt *stmt)
                                                                RelationGetRelationName(rel))));
                        break;
 
+               case OBJECT_FOREIGN_TABLE:
+                       if (rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                                errmsg("\"%s\" is not a foreign table",
+                                                               RelationGetRelationName(rel))));
+                       break;
+
                default:
                        elog(ERROR, "unrecognized object type: %d", (int) stmt->relkind);
        }
@@ -2526,6 +2575,7 @@ AlterTableGetLockLevel(List *cmds)
                        case AT_SetTableSpace:          /* must rewrite heap */
                        case AT_DropNotNull:            /* may change some SQL plans */
                        case AT_SetNotNull:
+                       case AT_GenericOptions:
                                cmd_lockmode = AccessExclusiveLock;
                                break;
 
@@ -2684,14 +2734,15 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
        switch (cmd->subtype)
        {
                case AT_AddColumn:              /* ADD COLUMN */
-                       ATSimplePermissions(rel, false, true);
+                       ATSimplePermissions(rel,
+                               ATT_TABLE|ATT_COMPOSITE_TYPE|ATT_FOREIGN_TABLE);
                        /* Performs own recursion */
                        ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
                        pass = AT_PASS_ADD_COL;
                        break;
                case AT_AddColumnToView:                /* add column via CREATE OR REPLACE
                                                                                 * VIEW */
-                       ATSimplePermissions(rel, true, false);
+                       ATSimplePermissions(rel, ATT_VIEW);
                        /* Performs own recursion */
                        ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
                        pass = AT_PASS_ADD_COL;
@@ -2704,19 +2755,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                         * substitutes default values into INSERTs before it expands
                         * rules.
                         */
-                       ATSimplePermissions(rel, true, false);
+                       ATSimplePermissions(rel, ATT_TABLE|ATT_VIEW);
                        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, ATT_TABLE|ATT_FOREIGN_TABLE);
                        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, ATT_TABLE|ATT_FOREIGN_TABLE);
                        ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
                        /* No command-specific prep needed */
                        pass = AT_PASS_ADD_CONSTR;
@@ -2729,30 +2780,31 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        break;
                case AT_SetOptions:             /* ALTER COLUMN SET ( options ) */
                case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
-                       ATSimplePermissionsRelationOrIndex(rel);
+                       ATSimplePermissions(rel, ATT_TABLE|ATT_INDEX);
                        /* This command never recurses */
                        pass = AT_PASS_MISC;
                        break;
                case AT_SetStorage:             /* ALTER COLUMN SET STORAGE */
-                       ATSimplePermissions(rel, false, false);
+                       ATSimplePermissions(rel, ATT_TABLE);
                        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,
+                               ATT_TABLE|ATT_COMPOSITE_TYPE|ATT_FOREIGN_TABLE);
                        ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
                        /* Recursion occurs during execution phase */
                        pass = AT_PASS_DROP;
                        break;
                case AT_AddIndex:               /* ADD INDEX */
-                       ATSimplePermissions(rel, false, false);
+                       ATSimplePermissions(rel, ATT_TABLE);
                        /* 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, ATT_TABLE);
                        /* Recursion occurs during execution phase */
                        /* No command-specific prep needed except saving recurse flag */
                        if (recurse)
@@ -2760,7 +2812,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, ATT_TABLE);
                        /* Recursion occurs during execution phase */
                        /* No command-specific prep needed except saving recurse flag */
                        if (recurse)
@@ -2768,7 +2820,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        pass = AT_PASS_DROP;
                        break;
                case AT_AlterColumnType:                /* ALTER COLUMN TYPE */
-                       ATSimplePermissions(rel, false, true);
+                       ATSimplePermissions(rel,
+                               ATT_TABLE|ATT_COMPOSITE_TYPE|ATT_FOREIGN_TABLE);
                        /* Performs own recursion */
                        ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
                        pass = AT_PASS_ALTER_TYPE;
@@ -2780,20 +2833,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, ATT_TABLE);
                        /* 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, ATT_TABLE);
                        /* 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, ATT_TABLE);
                        /* Performs own recursion */
                        if (rel->rd_rel->relhasoids)
                        {
@@ -2807,20 +2860,20 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                        pass = AT_PASS_DROP;
                        break;
                case AT_SetTableSpace:  /* SET TABLESPACE */
-                       ATSimplePermissionsRelationOrIndex(rel);
+                       ATSimplePermissions(rel, ATT_TABLE|ATT_INDEX);
                        /* This command never recurses */
                        ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
                        pass = AT_PASS_MISC;    /* doesn't actually matter */
                        break;
                case AT_SetRelOptions:  /* SET (...) */
                case AT_ResetRelOptions:                /* RESET (...) */
-                       ATSimplePermissionsRelationOrIndex(rel);
+                       ATSimplePermissions(rel, ATT_TABLE|ATT_INDEX);
                        /* This command never recurses */
                        /* No command-specific prep needed */
                        pass = AT_PASS_MISC;
                        break;
                case AT_AddInherit:             /* INHERIT */
-                       ATSimplePermissions(rel, false, false);
+                       ATSimplePermissions(rel, ATT_TABLE);
                        /* This command never recurses */
                        ATPrepAddInherit(rel);
                        pass = AT_PASS_MISC;
@@ -2837,12 +2890,21 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
                case AT_EnableAlwaysRule:
                case AT_EnableReplicaRule:
                case AT_DisableRule:
-               case AT_DropInherit:    /* NO INHERIT */
-                       ATSimplePermissions(rel, false, false);
+                       ATSimplePermissions(rel, ATT_TABLE);
                        /* These commands never recurse */
                        /* No command-specific prep needed */
                        pass = AT_PASS_MISC;
                        break;
+               case AT_DropInherit:    /* NO INHERIT */
+                       ATSimplePermissions(rel, ATT_TABLE);
+                       /* No command-specific prep needed */
+                       pass = AT_PASS_MISC;
+                       break;
+               case AT_GenericOptions:
+                       ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
+                       /* No command-specific prep needed */
+                       pass = AT_PASS_MISC;
+                       break;
                default:                                /* oops */
                        elog(ERROR, "unrecognized alter table type: %d",
                                 (int) cmd->subtype);
@@ -3085,6 +3147,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                case AT_DropInherit:
                        ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
                        break;
+               case AT_GenericOptions:
+                       ATExecGenericOptions(rel, (List *) cmd->def);
+                       break;
                default:                                /* oops */
                        elog(ERROR, "unrecognized alter table type: %d",
                                 (int) cmd->subtype);
@@ -3111,6 +3176,10 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
        {
                AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
 
+               /* Foreign tables have no storage. */
+               if (tab->relkind == RELKIND_FOREIGN_TABLE)
+                       continue;
+
                /*
                 * We only need to rewrite the table if at least one column needs to
                 * be recomputed, or we are adding/removing the OID column.
@@ -3564,33 +3633,36 @@ ATGetQueueEntry(List **wqueue, Relation rel)
  * - Ensure that it is not a system table
  */
 static void
-ATSimplePermissions(Relation rel, bool allowView, bool allowType)
+ATSimplePermissions(Relation rel, int allowed_targets)
 {
-       if (rel->rd_rel->relkind != RELKIND_RELATION)
+       int             actual_target;
+
+       switch (rel->rd_rel->relkind)
        {
-               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)
-                               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))));
+               case RELKIND_RELATION:
+                       actual_target = ATT_TABLE;
+                       break;
+               case RELKIND_VIEW:
+                       actual_target = ATT_VIEW;
+                       break;
+               case RELKIND_INDEX:
+                       actual_target = ATT_INDEX;
+                       break;
+               case RELKIND_COMPOSITE_TYPE:
+                       actual_target = ATT_COMPOSITE_TYPE;
+                       break;
+               case RELKIND_FOREIGN_TABLE:
+                       actual_target = ATT_FOREIGN_TABLE;
+                       break;
+               default:
+                       actual_target = 0;
+                       break;
        }
 
+       /* Wrong target type? */
+       if ((actual_target & allowed_targets) == 0)
+               ATWrongRelkindError(rel, allowed_targets);
+
        /* Permissions checks */
        if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
@@ -3604,32 +3676,48 @@ ATSimplePermissions(Relation rel, bool allowView, bool allowType)
 }
 
 /*
- * ATSimplePermissionsRelationOrIndex
+ * ATWrongRelkindError
  *
- * - Ensure that it is a relation or an index
- * - Ensure this user is the owner
- * - Ensure that it is not a system table
+ * Throw an error when a relation has been determined to be of the wrong
+ * type.
  */
 static void
-ATSimplePermissionsRelationOrIndex(Relation rel)
+ATWrongRelkindError(Relation rel, int allowed_targets)
 {
-       if (rel->rd_rel->relkind != RELKIND_RELATION &&
-               rel->rd_rel->relkind != RELKIND_INDEX)
-               ereport(ERROR,
-                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                errmsg("\"%s\" is not a table or index",
-                                               RelationGetRelationName(rel))));
+       char       *msg;
 
-       /* Permissions checks */
-       if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
-               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
-                                          RelationGetRelationName(rel));
+       switch (allowed_targets)
+       {
+               case ATT_TABLE:
+                       msg = _("\"%s\" is not a table");
+                       break;
+               case ATT_TABLE|ATT_INDEX:
+                       msg = _("\"%s\" is not a table or index");
+                       break;
+               case ATT_TABLE|ATT_VIEW:
+                       msg = _("\"%s\" is not a table or view");
+                       break;
+               case ATT_TABLE|ATT_FOREIGN_TABLE:
+                       msg = _("\"%s\" is not a table or foreign table");
+                       break;
+               case ATT_TABLE|ATT_COMPOSITE_TYPE|ATT_FOREIGN_TABLE:
+                       msg = _("\"%s\" is not a table, composite type, or foreign table");
+                       break;
+               case ATT_VIEW:
+                       msg = _("\"%s\" is not a view");
+                       break;
+               case ATT_FOREIGN_TABLE:
+                       msg = _("\"%s\" is not a foreign table");
+                       break;
+               default:
+                       /* shouldn't get here, add all necessary cases above */
+                       msg = _("\"%s\" is of the wrong type");
+                       break;
+       }
 
-       if (!allowSystemTableMods && IsSystemRelation(rel))
-               ereport(ERROR,
-                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                errmsg("permission denied: \"%s\" is a system catalog",
-                                               RelationGetRelationName(rel))));
+       ereport(ERROR,
+                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                        errmsg(msg, RelationGetRelationName(rel))));
 }
 
 /*
@@ -4101,6 +4189,11 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
        {
                RawColumnDefault *rawEnt;
 
+               if (relkind == RELKIND_FOREIGN_TABLE)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("default values on foreign tables are not supported")));
+
                rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
                rawEnt->attnum = attribute.attnum;
                rawEnt->raw_default = copyObject(colDef->raw_default);
@@ -4137,12 +4230,14 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
         * returned by AddRelationNewConstraints, so that the right thing happens
         * when a datatype's default applies.
         *
-        * We skip this step completely for views.      For a view, we can only get
-        * here from CREATE OR REPLACE VIEW, which historically doesn't set up
-        * defaults, not even for domain-typed columns.  And in any case we
-        * mustn't invoke Phase 3 on a view, since it has no storage.
+        * We skip this step completely for views and foreign tables.  For a view,
+        * we can only get here from CREATE OR REPLACE VIEW, which historically
+        * doesn't set up defaults, not even for domain-typed columns.  And in any
+        * case we mustn't invoke Phase 3 on a view or foreign table, since they
+        * have no storage.
         */
-       if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE && attribute.attnum > 0)
+       if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE
+               && relkind != RELKIND_FOREIGN_TABLE && attribute.attnum > 0)
        {
                defval = (Expr *) build_column_default(rel, attribute.attnum);
 
@@ -4692,7 +4787,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, ATT_TABLE);
 
        /*
         * get the number of the attribute
@@ -4996,7 +5091,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, ATT_TABLE);
 
        /*
         * Call AddRelationNewConstraints to do the work, making sure it works on
@@ -5947,7 +6042,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, ATT_TABLE);
 
        conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
 
@@ -6252,6 +6347,13 @@ ATPrepAlterColumnType(List **wqueue,
 
                tab->newvals = lappend(tab->newvals, newval);
        }
+       else if (tab->relkind == RELKIND_FOREIGN_TABLE)
+       {
+               if (cmd->transform)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("ALTER TYPE USING is not supported on foreign tables")));
+       }
 
        if (tab->relkind == RELKIND_COMPOSITE_TYPE)
        {
@@ -6834,6 +6936,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:
@@ -6890,7 +6993,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))));
        }
 
@@ -7550,7 +7653,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, ATT_TABLE);
 
        /* Permanent rels cannot inherit from temporary ones */
        if (RelationUsesTempNamespace(parent_rel)
@@ -8108,6 +8211,76 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
        heap_close(parent_rel, NoLock);
 }
 
+/*
+ * ALTER FOREIGN TABLE <name> OPTIONS (...)
+ */
+static void
+ATExecGenericOptions(Relation rel, List *options)
+{
+       Relation                ftrel;
+       ForeignServer  *server;
+       ForeignDataWrapper *fdw;
+       HeapTuple               tuple;
+       bool                    isnull;
+       Datum                   repl_val[Natts_pg_foreign_table];
+       bool                    repl_null[Natts_pg_foreign_table];
+       bool                    repl_repl[Natts_pg_foreign_table];
+       Datum                   datum;
+       Form_pg_foreign_table   tableform;
+
+       if (options == NIL) 
+               return;
+
+       ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+       tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
+       if (!HeapTupleIsValid(tuple))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("foreign table \"%s\" does not exist",
+                                                                               RelationGetRelationName(rel))));
+       tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
+       server = GetForeignServer(tableform->ftserver);
+       fdw = GetForeignDataWrapper(server->fdwid);
+
+       memset(repl_val, 0, sizeof(repl_val));
+       memset(repl_null, false, sizeof(repl_null));
+       memset(repl_repl, false, sizeof(repl_repl));
+
+       /* Extract the current options */
+       datum = SysCacheGetAttr(FOREIGNTABLEREL,
+                                                       tuple,
+                                                       Anum_pg_foreign_table_ftoptions,
+                                                       &isnull);
+       if (isnull)
+               datum = PointerGetDatum(NULL);
+
+       /* Transform the options */
+       datum = transformGenericOptions(ForeignTableRelationId,
+                                                                       datum,
+                                                                       options,
+                                                                       fdw->fdwvalidator);
+
+       if (PointerIsValid(DatumGetPointer(datum)))
+               repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
+       else
+               repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+       repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+       /* Everything looks good - update the tuple */
+
+       tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
+                                                         repl_val, repl_null, repl_repl);
+
+       simple_heap_update(ftrel, &tuple->t_self, tuple);
+       CatalogUpdateIndexes(ftrel, tuple);
+
+       heap_close(ftrel, RowExclusiveLock);
+
+       heap_freetuple(tuple);
+}
+
 
 /*
  * Execute ALTER TABLE SET SCHEMA
@@ -8156,6 +8329,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);
        }
@@ -8165,6 +8346,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:
@@ -8195,7 +8377,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 410ef5ded72829c9e496dd0cc817d8be00ca1f48..82d9775b2405639fd7e8c3a0f3951ccac22109d5 100644 (file)
@@ -894,7 +894,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
                onerel->rd_rel->relkind != RELKIND_TOASTVALUE)
        {
                ereport(WARNING,
-                               (errmsg("skipping \"%s\" --- cannot vacuum indexes, views, or special system tables",
+                               (errmsg("skipping \"%s\" --- cannot only non-tables or special system tables",
                                                RelationGetRelationName(onerel))));
                relation_close(onerel, lmode);
                PopActiveSnapshot();
index 3eaef2a6c3574b8073371d00bcb594f7b80bb1c5..17c7ea294dc915bf1bd4504da7caa26162afc217 100644 (file)
@@ -934,6 +934,12 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
                                        break;
                        }
                        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 a968e375a6c32953dff4fb792a5de90cb4edfd6e..804f900c7eb6fc380d086d7e54db998f12873562 100644 (file)
@@ -2575,11 +2575,15 @@ _copyCopyStmt(CopyStmt *from)
        return newnode;
 }
 
-static CreateStmt *
-_copyCreateStmt(CreateStmt *from)
+/*
+ * CopyCreateStmtFields
+ *
+ *             This function copies the fields of the CreateStmt node.  It is used by
+ *             copy functions for classes which inherit from CreateStmt.
+ */
+static void
+CopyCreateStmtFields(CreateStmt *from, CreateStmt *newnode)
 {
-       CreateStmt *newnode = makeNode(CreateStmt);
-
        COPY_NODE_FIELD(relation);
        COPY_NODE_FIELD(tableElts);
        COPY_NODE_FIELD(inhRelations);
@@ -2589,6 +2593,14 @@ _copyCreateStmt(CreateStmt *from)
        COPY_SCALAR_FIELD(oncommit);
        COPY_STRING_FIELD(tablespacename);
        COPY_SCALAR_FIELD(if_not_exists);
+}
+
+static CreateStmt *
+_copyCreateStmt(CreateStmt *from)
+{
+       CreateStmt *newnode = makeNode(CreateStmt);
+
+       CopyCreateStmtFields(from, newnode);
 
        return newnode;
 }
@@ -3297,6 +3309,19 @@ _copyDropUserMappingStmt(DropUserMappingStmt *from)
        return newnode;
 }
 
+static CreateForeignTableStmt *
+_copyCreateForeignTableStmt(CreateForeignTableStmt *from)
+{
+       CreateForeignTableStmt *newnode = makeNode(CreateForeignTableStmt);
+
+       CopyCreateStmtFields((CreateStmt *) from, (CreateStmt *) newnode);
+
+       COPY_STRING_FIELD(servername);
+       COPY_NODE_FIELD(options);
+
+       return newnode;
+}
+
 static CreateTrigStmt *
 _copyCreateTrigStmt(CreateTrigStmt *from)
 {
@@ -4198,6 +4223,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 9e7c9481bd72c52605a3091fa887980b339ccb8b..2ef1a33bb7b536e2c520e023708a41e538bbb9c4 100644 (file)
@@ -1718,6 +1718,18 @@ _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(servername);
+       COMPARE_NODE_FIELD(options);
+
+       return true;
+}
+
 static bool
 _equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
 {
@@ -2821,6 +2833,9 @@ equal(void *a, void *b)
                case T_DropUserMappingStmt:
                        retval = _equalDropUserMappingStmt(a, b);
                        break;
+               case T_CreateForeignTableStmt:
+                       retval = _equalCreateForeignTableStmt(a, b);
+                       break;
                case T_CreateTrigStmt:
                        retval = _equalCreateTrigStmt(a, b);
                        break;
index 88256636662bfc29230785f89b0774b1eaf61ef6..c85fc2d9d48d83e965d595e9609d8f0061426b4e 100644 (file)
@@ -1856,6 +1856,17 @@ _outCreateStmt(StringInfo str, CreateStmt *node)
        WRITE_BOOL_FIELD(if_not_exists);
 }
 
+static void
+_outCreateForeignTableStmt(StringInfo str, CreateForeignTableStmt *node)
+{
+       WRITE_NODE_TYPE("CREATEFOREIGNTABLESTMT");
+
+       _outCreateStmt(str, (CreateStmt *) &node->base);
+
+       WRITE_STRING_FIELD(servername);
+       WRITE_NODE_FIELD(options);
+}
+
 static void
 _outIndexStmt(StringInfo str, IndexStmt *node)
 {
@@ -2881,6 +2892,9 @@ _outNode(StringInfo str, void *obj)
                        case T_CreateStmt:
                                _outCreateStmt(str, obj);
                                break;
+                       case T_CreateForeignTableStmt:
+                               _outCreateForeignTableStmt(str, obj);
+                               break;
                        case T_IndexStmt:
                                _outIndexStmt(str, obj);
                                break;
index f0504e1397fa48825b4119681425e676c1798bc4..adbe45caec49e7189684debfc00cd4c2e739f08b 100644 (file)
@@ -90,6 +90,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
         */
        relation = heap_open(relationObjectId, NoLock);
 
+       /* Foreign table scans are not implemented yet. */
+       if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("foreign table scans are not yet supported")));
+
        rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1;
        rel->max_attr = RelationGetNumberOfAttributes(relation);
        rel->reltablespace = RelationGetForm(relation)->reltablespace;
index 8c89d5c5f7455d39da537d895defeeb8594e6dfb..43e8fdbd724d696adba6913bd810b1a22a85e5d6 100644 (file)
@@ -185,6 +185,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
                AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
                AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
                AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
+               AlterForeignTableStmt
                AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
                AlterRoleStmt AlterRoleSetStmt
                AlterDefaultPrivilegesStmt DefACLAction
@@ -193,7 +194,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
                CreateDomainStmt CreateGroupStmt CreateOpClassStmt
                CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
                CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
-               CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
+               CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
+               CreateAssertStmt CreateTrigStmt
                CreateUserStmt CreateUserMappingStmt CreateRoleStmt
                CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
                DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
@@ -279,6 +281,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <list>   stmtblock stmtmulti
                                OptTableElementList TableElementList OptInherit definition
                                OptTypedTableElementList TypedTableElementList
+                               OptForeignTableElementList ForeignTableElementList
                                reloptions opt_reloptions
                                OptWith opt_distinct opt_definition func_args func_args_list
                                func_args_with_defaults func_args_with_defaults_list
@@ -351,6 +354,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type <vsetstmt> set_rest SetResetClause
 
 %type <node>   TableElement TypedTableElement ConstraintElem TableFuncElement
+                               ForeignTableElement
 %type <node>   columnDef columnOptions
 %type <defelt> def_elem reloption_elem old_aggr_elem
 %type <node>   def_arg columnElem where_clause where_or_current_clause
@@ -658,6 +662,7 @@ stmt :
                        | AlterEnumStmt
                        | AlterFdwStmt
                        | AlterForeignServerStmt
+                       | AlterForeignTableStmt
                        | AlterFunctionStmt
                        | AlterGroupStmt
                        | AlterObjectSchemaStmt
@@ -686,6 +691,7 @@ stmt :
                        | CreateDomainStmt
                        | CreateFdwStmt
                        | CreateForeignServerStmt
+                       | CreateForeignTableStmt
                        | CreateFunctionStmt
                        | CreateGroupStmt
                        | CreateOpClassStmt
@@ -1935,6 +1941,13 @@ alter_table_cmd:
                                        n->def = (Node *)$2;
                                        $$ = (Node *)n;
                                }
+                       | alter_generic_options
+                               {
+                                       AlterTableCmd *n = makeNode(AlterTableCmd);
+                                       n->subtype = AT_GenericOptions;
+                                       n->def = (Node *)$1;
+                                       $$ = (Node *) n;
+                               }
                ;
 
 alter_column_default:
@@ -3380,6 +3393,84 @@ AlterForeignServerStmt: ALTER SERVER name foreign_server_version alter_generic_o
                                }
                ;
 
+/*****************************************************************************
+ *
+ *             QUERY:
+ *             CREATE FOREIGN TABLE relname (...) SERVER name (...)
+ *
+ *****************************************************************************/
+
+CreateForeignTableStmt:
+               CREATE FOREIGN TABLE qualified_name
+                       OptForeignTableElementList
+                       SERVER name create_generic_options
+                               {
+                                       CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt);
+                                       $4->relpersistence = RELPERSISTENCE_PERMANENT;
+                                       n->base.relation = $4;
+                                       n->base.tableElts = $5;
+                                       n->base.inhRelations = NIL;
+                                       n->base.if_not_exists = false;
+                                       /* FDW-specific data */
+                                       n->servername = $7;
+                                       n->options = $8;
+                                       $$ = (Node *) n;
+                               }
+               | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name
+                       OptForeignTableElementList
+                       SERVER name create_generic_options
+                               {
+                                       CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt);
+                                       $7->relpersistence = RELPERSISTENCE_PERMANENT;
+                                       n->base.relation = $7;
+                                       n->base.tableElts = $8;
+                                       n->base.inhRelations = NIL;
+                                       n->base.if_not_exists = true;
+                                       /* FDW-specific data */
+                                       n->servername = $10;
+                                       n->options = $11;
+                                       $$ = (Node *) n;
+                               }
+               ;
+
+OptForeignTableElementList:
+                       '(' ForeignTableElementList ')'                 { $$ = $2; }
+                       | '(' ')'                                                               { $$ = NIL; }
+               ;
+
+ForeignTableElementList:
+                       ForeignTableElement
+                               {
+                                       $$ = list_make1($1);
+                               }
+                       | ForeignTableElementList ',' ForeignTableElement
+                               {
+                                       $$ = lappend($1, $3);
+                               }
+               ;
+
+ForeignTableElement:
+                       columnDef                                       { $$ = $1; }
+               ;
+
+/*****************************************************************************
+ *
+ *             QUERY:
+ *             ALTER FOREIGN TABLE relname [...]
+ *
+ *****************************************************************************/
+
+AlterForeignTableStmt:
+                       ALTER FOREIGN TABLE relation_expr alter_table_cmds
+                               {
+                                       AlterTableStmt *n = makeNode(AlterTableStmt);
+                                       n->relation = $4;
+                                       n->cmds = $5;
+                                       n->relkind = OBJECT_FOREIGN_TABLE;
+                                       $$ = (Node *)n;
+                               }
+               ;
+
 /*****************************************************************************
  *
  *             QUERY:
@@ -4189,6 +4280,7 @@ drop_type:        TABLE                                                                   { $$ = OBJECT_TABLE; }
                        | VIEW                                                                  { $$ = OBJECT_VIEW; }
                        | INDEX                                                                 { $$ = OBJECT_INDEX; }
                        | TYPE_P                                                                { $$ = OBJECT_TYPE; }
+                       | FOREIGN TABLE                                                 { $$ = OBJECT_FOREIGN_TABLE; }
                        | DOMAIN_P                                                              { $$ = OBJECT_DOMAIN; }
                        | CONVERSION_P                                                  { $$ = OBJECT_CONVERSION; }
                        | SCHEMA                                                                { $$ = OBJECT_SCHEMA; }
@@ -4247,8 +4339,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) |
@@ -4425,6 +4517,7 @@ comment_type:
                        | CONVERSION_P                                          { $$ = OBJECT_CONVERSION; }
                        | TABLESPACE                                            { $$ = OBJECT_TABLESPACE; }
                        | ROLE                                                          { $$ = OBJECT_ROLE; }
+                       | FOREIGN TABLE                                         { $$ = OBJECT_FOREIGN_TABLE; }
                ;
 
 comment_text:
@@ -4506,6 +4599,7 @@ opt_provider:     FOR ColId_or_Sconst     { $$ = $2; }
 
 security_label_type:
                        COLUMN                                                          { $$ = OBJECT_COLUMN; }
+                       | FOREIGN TABLE                                         { $$ = OBJECT_FOREIGN_TABLE; }
                        | SCHEMA                                                        { $$ = OBJECT_SCHEMA; }
                        | SEQUENCE                                                      { $$ = OBJECT_SEQUENCE; }
                        | TABLE                                                         { $$ = OBJECT_TABLE; }
@@ -4841,6 +4935,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));
@@ -5927,15 +6029,35 @@ 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);
                                        n->renameType = OBJECT_COLUMN;
+                                       n->relationType = OBJECT_TABLE;
                                        n->relation = $3;
                                        n->subname = $6;
                                        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->relationType = OBJECT_FOREIGN_TABLE;
+                                       n->relation = $4;
+                                       n->subname = $7;
+                                       n->newname = $9;
+                                       $$ = (Node *)n;
+                               }
                        | ALTER TRIGGER name ON qualified_name RENAME TO name
                                {
                                        RenameStmt *n = makeNode(RenameStmt);
@@ -6031,6 +6153,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
                                {
                                        RenameStmt *n = makeNode(RenameStmt);
                                        n->renameType = OBJECT_ATTRIBUTE;
+                                       n->relationType = OBJECT_TYPE;
                                        n->relation = makeRangeVarFromAnyName($3, @3, yyscanner);
                                        n->subname = $6;
                                        n->newname = $8;
@@ -6171,6 +6294,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 d75c706816b39a053304167483a60fbacc323370..23c60eec318548361bf76e0627a1be32e976838d 100644 (file)
@@ -65,7 +65,7 @@
 /* State shared by transformCreateStmt and its subroutines */
 typedef struct
 {
-       const char *stmtType;           /* "CREATE TABLE" or "ALTER TABLE" */
+       const char *stmtType;           /* "CREATE [FOREIGN] TABLE" or "ALTER TABLE" */
        RangeVar   *relation;           /* relation to create */
        Relation        rel;                    /* opened/locked rel, if ALTER */
        List       *inhRelations;       /* relations to inherit from */
@@ -173,7 +173,10 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
        pstate = make_parsestate(NULL);
        pstate->p_sourcetext = queryString;
 
-       cxt.stmtType = "CREATE TABLE";
+       if (IsA(stmt, CreateForeignTableStmt))
+               cxt.stmtType = "CREATE FOREIGN TABLE";
+       else
+               cxt.stmtType = "CREATE TABLE";
        cxt.relation = stmt->relation;
        cxt.rel = NULL;
        cxt.inhRelations = stmt->inhRelations;
index f4e41a17652b348caeb5be4c23675074c5c719fb..9500037770463d9b413ac5e2ac24efef28894bae 100644 (file)
@@ -220,6 +220,7 @@ check_xact_readonly(Node *parsetree)
                case T_AlterUserMappingStmt:
                case T_DropUserMappingStmt:
                case T_AlterTableSpaceOptionsStmt:
+               case T_CreateForeignTableStmt:
                case T_SecLabelStmt:
                        PreventCommandIfReadOnly(CreateCommandTag(parsetree));
                        break;
@@ -492,6 +493,7 @@ standard_ProcessUtility(Node *parsetree,
                        break;
 
                case T_CreateStmt:
+               case T_CreateForeignTableStmt:
                        {
                                List       *stmts;
                                ListCell   *l;
@@ -540,6 +542,22 @@ 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);
+
+                                               /*
+                                                * Unless "IF NOT EXISTS" was specified and the
+                                                * relation already exists, create the pg_foreign_table
+                                                * entry.
+                                                */
+                                               if (relOid != InvalidOid)
+                                                       CreateForeignTable((CreateForeignTableStmt *) stmt,
+                                                                                               relOid);
+                                       }
                                        else
                                        {
                                                /* Recurse for anything else */
@@ -618,6 +636,7 @@ standard_ProcessUtility(Node *parsetree,
                                        case OBJECT_SEQUENCE:
                                        case OBJECT_VIEW:
                                        case OBJECT_INDEX:
+                                       case OBJECT_FOREIGN_TABLE:
                                                RemoveRelations(stmt);
                                                break;
 
@@ -1557,6 +1576,10 @@ CreateCommandTag(Node *parsetree)
                        tag = "DROP USER MAPPING";
                        break;
 
+               case T_CreateForeignTableStmt:
+                       tag = "CREATE FOREIGN TABLE";
+                       break;
+
                case T_DropStmt:
                        switch (((DropStmt *) parsetree)->removeType)
                        {
@@ -1596,6 +1619,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_TSCONFIGURATION:
                                        tag = "DROP TEXT SEARCH CONFIGURATION";
                                        break;
+                               case OBJECT_FOREIGN_TABLE:
+                                       tag = "DROP FOREIGN TABLE";
+                                       break;
                                default:
                                        tag = "???";
                        }
@@ -1654,6 +1680,14 @@ CreateCommandTag(Node *parsetree)
                                        tag = "ALTER SEQUENCE";
                                        break;
                                case OBJECT_COLUMN:
+                                       {
+                                               RenameStmt *stmt = (RenameStmt *) parsetree;
+                                               if (stmt->relationType == OBJECT_FOREIGN_TABLE)
+                                                       tag = "ALTER FOREIGN TABLE";
+                                               else
+                                                       tag = "ALTER TABLE";
+                                       }
+                                       break;
                                case OBJECT_TABLE:
                                        tag = "ALTER TABLE";
                                        break;
@@ -1666,6 +1700,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_VIEW:
                                        tag = "ALTER VIEW";
                                        break;
+                               case OBJECT_FOREIGN_TABLE:
+                                       tag = "ALTER FOREIGN TABLE";
+                                       break;
                                case OBJECT_TSPARSER:
                                        tag = "ALTER TEXT SEARCH PARSER";
                                        break;
@@ -1736,6 +1773,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_VIEW:
                                        tag = "ALTER VIEW";
                                        break;
+                               case OBJECT_FOREIGN_TABLE:
+                                       tag = "ALTER FOREIGN TABLE";
+                                       break;
                                default:
                                        tag = "???";
                                        break;
@@ -1796,6 +1836,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_FOREIGN_SERVER:
                                        tag = "ALTER SERVER";
                                        break;
+                               case OBJECT_FOREIGN_TABLE:
+                                       tag = "ALTER FOREIGN TABLE";
+                                       break;
                                default:
                                        tag = "???";
                                        break;
@@ -1820,6 +1863,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_VIEW:
                                        tag = "ALTER VIEW";
                                        break;
+                               case OBJECT_FOREIGN_TABLE:
+                                       tag = "ALTER FOREIGN TABLE";
+                                       break;
                                default:
                                        tag = "???";
                                        break;
@@ -2316,6 +2362,7 @@ GetCommandLogLevel(Node *parsetree)
                        break;
 
                case T_CreateStmt:
+               case T_CreateForeignTableStmt:
                        lev = LOGSTMT_DDL;
                        break;
 
index 05c9c266ffa62ed0bcc80ee41b87b5716065a80f..691ba3bd95a510f37665d5ca187b5fdca232b020 100644 (file)
@@ -782,6 +782,10 @@ acldefault(GrantObjectType objtype, Oid ownerId)
                        world_default = ACL_NO_RIGHTS;
                        owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
                        break;
+               case ACL_OBJECT_FOREIGN_TABLE:
+                       world_default = ACL_NO_RIGHTS;
+                       owner_default = ACL_ALL_RIGHTS_FOREIGN_TABLE;
+                       break;
                default:
                        elog(ERROR, "unrecognized objtype: %d", (int) objtype);
                        world_default = ACL_NO_RIGHTS;          /* keep compiler quiet */
index cba130b55eeea6bc8790b1be2274e9cf3f64407b..191953b972c1cbfa09b68b283a4288f966cb68b6 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 e280b5de17432851aca4aecb02f132f3175ea821..0f814f142f275f8086d7e9317560046b131c32b8 100644 (file)
@@ -868,6 +868,8 @@ do { \
                CONVERT_PRIV('U', "USAGE");
        else if (strcmp(type, "FOREIGN SERVER") == 0)
                CONVERT_PRIV('U', "USAGE");
+       else if (strcmp(type, "FOREIGN TABLE") == 0)
+               CONVERT_PRIV('r', "SELECT");
        else if (strcmp(type, "LARGE OBJECT") == 0)
        {
                CONVERT_PRIV('r', "SELECT");
index e1f3b055deffea5e3019e571532a42bbfb89884f..64d8d93dda81805540ac70dbd7afcf11390c19fc 100644 (file)
@@ -2726,6 +2726,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
                strcmp(type, "DOMAIN") == 0 ||
                strcmp(type, "TABLE") == 0 ||
                strcmp(type, "TYPE") == 0 ||
+               strcmp(type, "FOREIGN TABLE") == 0 ||
                strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
                strcmp(type, "TEXT SEARCH CONFIGURATION") == 0)
        {
@@ -2918,6 +2919,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
                        strcmp(te->desc, "TYPE") == 0 ||
                        strcmp(te->desc, "VIEW") == 0 ||
                        strcmp(te->desc, "SEQUENCE") == 0 ||
+                       strcmp(te->desc, "FOREIGN TABLE") == 0 ||
                        strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
                        strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0 ||
                        strcmp(te->desc, "FOREIGN DATA WRAPPER") == 0 ||
index 08288e871a95b0b24ae6ad4e24181e0d60638913..b0a0dc56072c6b676e87b4395981b6b670c41f33 100644 (file)
@@ -975,8 +975,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)");
@@ -1476,6 +1477,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;
                /* Skip unlogged tables if so requested */
                if (tblinfo[i].relpersistence == RELPERSISTENCE_UNLOGGED
                        && no_unlogged_table_data)
@@ -3513,12 +3517,13 @@ getTables(int *numTables)
                                                  "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') "
+                                                 "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_VIEW, RELKIND_COMPOSITE_TYPE,
+                                                 RELKIND_FOREIGN_TABLE);
        }
        else if (g_fout->remoteVersion >= 90000)
        {
@@ -3871,7 +3876,7 @@ getTables(int *numTables)
                 * assume our lock on the child is enough to prevent schema
                 * alterations to parent tables.
                 *
-                * NOTE: it'd be kinda nice to lock views and sequences too, not only
+                * NOTE: it'd be kinda nice to lock other relations too, not only
                 * plain tables, but the backend doesn't presently allow that.
                 */
                if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION)
@@ -10938,7 +10943,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);
@@ -11007,6 +11014,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
        int                     j,
                                k;
        bool            toast_set = false;
+       char       *srvname;
+       char       *ftoptions = NULL;
 
        /* Make sure we are in proper schema */
        selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
@@ -11080,7 +11089,35 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
        }
        else
        {
-               reltypename = "TABLE";
+               if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+               {
+                       int             i_srvname;
+                       int             i_ftoptions;
+
+                       reltypename = "FOREIGN TABLE";
+
+                       /* retrieve name of foreign server and generic options */ 
+                       appendPQExpBuffer(query,
+                               "SELECT fs.srvname, array_to_string(ARRAY("
+                               "   SELECT option_name || ' ' || quote_literal(option_value)"
+                               "   FROM pg_options_to_table(ftoptions)), ', ') AS ftoptions "
+                               "FROM pg_foreign_table ft JOIN pg_foreign_server fs "
+                               "       ON (fs.oid = ft.ftserver) "
+                               "WHERE ft.ftrelid = %u", tbinfo->dobj.catId.oid);
+                       res = PQexec(g_conn, query->data);
+                       check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+                       i_srvname = PQfnumber(res, "srvname");
+                       i_ftoptions = PQfnumber(res, "ftoptions");
+                       srvname = strdup(PQgetvalue(res, 0, i_srvname));
+                       ftoptions = strdup(PQgetvalue(res, 0, i_ftoptions));
+                       PQclear(res);
+               }
+               else
+               {
+                       reltypename = "TABLE";
+                       srvname = NULL;
+                       ftoptions = NULL;
+               }
                numParents = tbinfo->numParents;
                parents = tbinfo->parents;
 
@@ -11088,7 +11125,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));
@@ -11096,12 +11133,11 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
                if (binary_upgrade)
                        binary_upgrade_set_relfilenodes(q, tbinfo->dobj.catId.oid, false);
 
-               if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED)
-                       appendPQExpBuffer(q, "CREATE UNLOGGED TABLE %s",
-                                                         fmtId(tbinfo->dobj.name));
-               else
-                       appendPQExpBuffer(q, "CREATE TABLE %s",
-                                                         fmtId(tbinfo->dobj.name));
+               appendPQExpBuffer(q, "CREATE %s%s %s",
+                                                 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
+                                                               "UNLOGGED " : "",
+                                                 reltypename,
+                                                 fmtId(tbinfo->dobj.name));
                if (tbinfo->reloftype)
                        appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
                actual_atts = 0;
@@ -11235,6 +11271,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))
                {
@@ -11254,6 +11293,10 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
                        appendPQExpBuffer(q, ")");
                }
 
+               /* Dump generic options if any */
+               if (ftoptions && ftoptions[0])
+                       appendPQExpBuffer(q, "\nOPTIONS (%s)", ftoptions);
+
                appendPQExpBuffer(q, ";\n");
 
                /*
@@ -11268,7 +11311,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
                 * order.  That also means we have to take care about setting
                 * attislocal correctly, plus fix up any inherited CHECK constraints.
                 */
-               if (binary_upgrade)
+               if (binary_upgrade && tbinfo->relkind == RELKIND_RELATION)
                {
                        for (j = 0; j < tbinfo->numatts; j++)
                        {
index 04be60f73e6c5a5896737d95e7ddd5732f8525fb..962c13c13606360fe358657e474a412266379eed 100644 (file)
@@ -369,7 +369,7 @@ exec_command(const char *cmd,
                                        success = describeTableDetails(pattern, show_verbose, show_system);
                                else
                                        /* standard listing of interesting things */
-                                       success = listTables("tvs", NULL, show_verbose, show_system);
+                                       success = listTables("tvsE", NULL, show_verbose, show_system);
                                break;
                        case 'a':
                                success = describeAggregates(pattern, show_verbose, show_system);
@@ -432,6 +432,7 @@ exec_command(const char *cmd,
                        case 'v':
                        case 'i':
                        case 's':
+                       case 'E':
                                success = listTables(&cmd[1], pattern, show_verbose, show_system);
                                break;
                        case 'r':
@@ -483,6 +484,9 @@ exec_command(const char *cmd,
                                        case 'w':
                                                success = listForeignDataWrappers(pattern, show_verbose);
                                                break;
+                                       case 't':
+                                               success = listForeignTables(pattern, show_verbose);
+                                               break;
                                        default:
                                                status = PSQL_CMD_UNKNOWN;
                                                break;
index 19ba517d1baf140389d2a3dfb58e195048d62b8a..205190f729e48e1fb7ff27d56ca4bb59e73e45fa 100644 (file)
@@ -687,11 +687,12 @@ permissionsList(const char *pattern)
        printfPQExpBuffer(&buf,
                                          "SELECT n.nspname as \"%s\",\n"
                                          "  c.relname as \"%s\",\n"
-                                         "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' END as \"%s\",\n"
+                                         "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
                                          "  ",
                                          gettext_noop("Schema"),
                                          gettext_noop("Name"),
           gettext_noop("table"), gettext_noop("view"), gettext_noop("sequence"),
+          gettext_noop("foreign table"),
                                          gettext_noop("Type"));
 
        printACLColumn(&buf, "c.relacl");
@@ -707,7 +708,7 @@ permissionsList(const char *pattern)
 
        appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_class c\n"
           "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
-                                         "WHERE c.relkind IN ('r', 'v', 'S')\n");
+                                         "WHERE c.relkind IN ('r', 'v', 'S', 'f')\n");
 
        /*
         * Unless a schema pattern is specified, we suppress system and temp
@@ -921,15 +922,16 @@ objectDescription(const char *pattern, bool showSystem)
                                          "  n.nspname as nspname,\n"
                                          "  CAST(c.relname AS pg_catalog.text) as name,\n"
                                          "  CAST(\n"
-                                         "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' END"
+                                         "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END"
                                          "  AS pg_catalog.text) as object\n"
                                          "  FROM pg_catalog.pg_class c\n"
         "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
-                                         "  WHERE c.relkind IN ('r', 'v', 'i', 'S')\n",
+                                         "  WHERE c.relkind IN ('r', 'v', 'i', 'S', 'f')\n",
                                          gettext_noop("table"),
                                          gettext_noop("view"),
                                          gettext_noop("index"),
-                                         gettext_noop("sequence"));
+                                         gettext_noop("sequence"),
+                                         gettext_noop("foreign table"));
 
        if (!showSystem && !pattern)
                appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
@@ -1325,6 +1327,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\"",
@@ -1337,7 +1343,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");
@@ -1565,7 +1572,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;
@@ -1984,11 +1991,36 @@ describeOneTableDetails(const char *schemaname,
        /*
         * Finish printing the footer information about a table.
         */
-       if (tableinfo.relkind == 'r')
+       if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f')
        {
                PGresult   *result;
                int                     tuples;
 
+               /* print foreign server name */
+               if (tableinfo.relkind == 'f')
+               {
+                       /* Footer information about foreign table */
+                       printfPQExpBuffer(&buf,
+                                                         "SELECT s.srvname\n"
+                                                         "FROM pg_catalog.pg_foreign_table f,\n"
+                                                         "     pg_catalog.pg_foreign_server s\n"
+                                                         "WHERE f.ftrelid = %s AND s.oid = f.ftserver",
+                                                         oid);
+                       result = PSQLexec(buf.data, false);
+                       if (!result)
+                               goto error_return;
+                       else if (PQntuples(result) != 1)
+                       {
+                               PQclear(result);
+                               goto error_return;
+                       }
+
+                       printfPQExpBuffer(&buf, "Server: %s",
+                               PQgetvalue(result, 0, 0));
+                       printTableAddFooter(&cont, buf.data);
+                       PQclear(result);
+               }
+
                /* print inherited tables */
                printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid);
 
@@ -2406,8 +2438,9 @@ listDbRoleSettings(const char *pattern, const char *pattern2)
  * i - indexes
  * v - views
  * s - sequences
+ * E - foreign table (Note: different from 'f', the relkind value)
  * (any order of the above is fine)
- * If tabtypes is empty, we default to \dtvs.
+ * If tabtypes is empty, we default to \dtvsE.
  */
 bool
 listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
@@ -2416,14 +2449,15 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
        bool            showIndexes = strchr(tabtypes, 'i') != NULL;
        bool            showViews = strchr(tabtypes, 'v') != NULL;
        bool            showSeq = strchr(tabtypes, 's') != NULL;
+       bool            showForeign = strchr(tabtypes, 'E') != NULL;
 
        PQExpBufferData buf;
        PGresult   *res;
        printQueryOpt myopt = pset.popt;
        static const bool translate_columns[] = {false, false, true, false, false, false, false};
 
-       if (!(showTables || showIndexes || showViews || showSeq))
-               showTables = showViews = showSeq = true;
+       if (!(showTables || showIndexes || showViews || showSeq || showForeign))
+               showTables = showViews = showSeq = showForeign = true;
 
        initPQExpBuffer(&buf);
 
@@ -2434,7 +2468,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"),
@@ -2443,6 +2477,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"));
 
@@ -2480,6 +2515,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");
 
@@ -3529,6 +3567,67 @@ listUserMappings(const char *pattern, bool verbose)
        return true;
 }
 
+/*
+ * \det
+ *
+ * Describes foreign tables.
+ */
+bool
+listForeignTables(const char *pattern, bool verbose)
+{
+       PQExpBufferData buf;
+       PGresult   *res;
+       printQueryOpt myopt = pset.popt;
+
+       if (pset.sversion < 90100)
+       {
+               fprintf(stderr, _("The server (version %d.%d) does not support foreign table.\n"),
+                               pset.sversion / 10000, (pset.sversion / 100) % 100);
+               return true;
+       }
+
+       initPQExpBuffer(&buf);
+       printfPQExpBuffer(&buf,
+                                         "SELECT n.nspname AS \"%s\",\n"
+                                         "  c.relname AS \"%s\",\n"
+                                         "  s.srvname AS \"%s\"",
+                                         gettext_noop("Schema"),
+                                         gettext_noop("Table"),
+                                         gettext_noop("Server"));
+
+       if (verbose)
+               appendPQExpBuffer(&buf,
+                                                 ",\n  ft.ftoptions AS \"%s\"",
+                                                 gettext_noop("Options"));
+
+       appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
+       appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
+       appendPQExpBuffer(&buf, "\n pg_catalog.pg_namespace n,");
+       appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s\n");
+       appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
+       appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
+       appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
+
+       processSQLNamePattern(pset.db, &buf, pattern, true, false,
+                                                 NULL, "n.nspname", "c.relname", NULL);
+
+       appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+       res = PSQLexec(buf.data, false);
+       termPQExpBuffer(&buf);
+       if (!res)
+               return false;
+
+       myopt.nullPrint = NULL;
+       myopt.title = _("List of foreign tables");
+       myopt.translate_header = true;
+
+       printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+       PQclear(res);
+       return true;
+}
+
 /*
  * printACLColumn
  *
index 6d9fada58f299901dac3dbafc70446e4d04ccd52..2029ef848dff2c1b0363b4440715278f2a44939e 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 8ab3b4722ec68d110c212d60f369eda7364fad7f..96c85a25d6c944ef5ddd53c24ceb0cddada4ebb3 100644 (file)
@@ -158,7 +158,7 @@ slashUsage(unsigned short int pager)
 {
        FILE       *output;
 
-       output = PageOutput(90, pager);
+       output = PageOutput(92, pager);
 
        /* if you add/remove a line here, change the row count above */
 
@@ -199,6 +199,7 @@ slashUsage(unsigned short int pager)
        fprintf(output, _("  \\dd[S]  [PATTERN]      show comments on objects\n"));
        fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
        fprintf(output, _("  \\dD[S]  [PATTERN]      list domains\n"));
+       fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));
        fprintf(output, _("  \\des[+] [PATTERN]      list foreign servers\n"));
        fprintf(output, _("  \\deu[+] [PATTERN]      list user mappings\n"));
        fprintf(output, _("  \\dew[+] [PATTERN]      list foreign-data wrappers\n"));
@@ -219,6 +220,7 @@ slashUsage(unsigned short int pager)
        fprintf(output, _("  \\dT[S+] [PATTERN]      list data types\n"));
        fprintf(output, _("  \\du[+]  [PATTERN]      list roles\n"));
        fprintf(output, _("  \\dv[S+] [PATTERN]      list views\n"));
+       fprintf(output, _("  \\dE[S+] [PATTERN]      list foreign tables\n"));
        fprintf(output, _("  \\l[+]                  list all databases\n"));
        fprintf(output, _("  \\sf[+] FUNCNAME        show a function's definition\n"));
        fprintf(output, _("  \\z      [PATTERN]      same as \\dp\n"));
index 2b140c150dafcbb4f7e17f795e0472a85e9dda53..414baa695d396e1dc08b09d27137dc44de0fa6cf 100644 (file)
@@ -288,6 +288,21 @@ static const SchemaQuery Query_for_list_of_sequences = {
        NULL
 };
 
+static const SchemaQuery Query_for_list_of_foreign_tables = {
+       /* catname */
+       "pg_catalog.pg_class c",
+       /* selcondition */
+       "c.relkind IN ('f')",
+       /* viscondition */
+       "pg_catalog.pg_table_is_visible(c.oid)",
+       /* namespace */
+       "c.relnamespace",
+       /* result */
+       "pg_catalog.quote_ident(c.relname)",
+       /* qualresult */
+       NULL
+};
+
 static const SchemaQuery Query_for_list_of_tables = {
        /* catname */
        "pg_catalog.pg_class c",
@@ -354,11 +369,11 @@ static const SchemaQuery Query_for_list_of_updatables = {
        NULL
 };
 
-static const SchemaQuery Query_for_list_of_tisv = {
+static const SchemaQuery Query_for_list_of_tisvf = {
        /* catname */
        "pg_catalog.pg_class c",
        /* selcondition */
-       "c.relkind IN ('r', 'i', 'S', 'v')",
+       "c.relkind IN ('r', 'i', 'S', 'v', 'f')",
        /* viscondition */
        "pg_catalog.pg_table_is_visible(c.oid)",
        /* namespace */
@@ -369,11 +384,11 @@ static const SchemaQuery Query_for_list_of_tisv = {
        NULL
 };
 
-static const SchemaQuery Query_for_list_of_tsv = {
+static const SchemaQuery Query_for_list_of_tsvf = {
        /* catname */
        "pg_catalog.pg_class c",
        /* selcondition */
-       "c.relkind IN ('r', 'S', 'v')",
+       "c.relkind IN ('r', 'S', 'v', 'f')",
        /* viscondition */
        "pg_catalog.pg_table_is_visible(c.oid)",
        /* namespace */
@@ -592,6 +607,7 @@ static const pgsql_thing_t words_after_create[] = {
        {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
        {"DOMAIN", NULL, &Query_for_list_of_domains},
        {"FOREIGN DATA WRAPPER", NULL, NULL},
+       {"FOREIGN TABLE", NULL, NULL},
        {"FUNCTION", NULL, &Query_for_list_of_functions},
        {"GROUP", Query_for_list_of_roles},
        {"LANGUAGE", Query_for_list_of_languages},
@@ -696,7 +712,7 @@ psql_completion(char *text, int start, int end)
 
        static const char *const backslash_commands[] = {
                "\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright",
-               "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df",
+               "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\df",
                "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
                "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
                "\\e", "\\echo", "\\ef", "\\encoding",
@@ -759,7 +775,7 @@ psql_completion(char *text, int start, int end)
                         pg_strcasecmp(prev3_wd, "TABLE") != 0)
        {
                static const char *const list_ALTER[] =
-               {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
+               {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
                        "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
                "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
 
@@ -822,6 +838,16 @@ psql_completion(char *text, int start, int end)
                COMPLETE_WITH_LIST(list_ALTERDATABASE);
        }
 
+       /* ALTER FOREIGN */
+       else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
+                        pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+       {
+               static const char *const list_ALTER_FOREIGN[] =
+               {"DATA WRAPPER", "TABLE", NULL};
+
+               COMPLETE_WITH_LIST(list_ALTER_FOREIGN);
+       }
+
        /* ALTER FOREIGN DATA WRAPPER <name> */
        else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
                         pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
@@ -834,6 +860,17 @@ psql_completion(char *text, int start, int end)
                COMPLETE_WITH_LIST(list_ALTER_FDW);
        }
 
+       /* ALTER FOREIGN TABLE <name> */
+       else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+                        pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
+                        pg_strcasecmp(prev2_wd, "TABLE") == 0)
+       {
+               static const char *const list_ALTER_FOREIGN_TABLE[] =
+               {"ALTER", "DROP", "RENAME", "OWNER TO", "SET SCHEMA", NULL};
+
+               COMPLETE_WITH_LIST(list_ALTER_FOREIGN_TABLE);
+       }
+
        /* ALTER INDEX <name> */
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
                         pg_strcasecmp(prev2_wd, "INDEX") == 0)
@@ -1448,7 +1485,7 @@ psql_completion(char *text, int start, int end)
                         pg_strcasecmp(prev_wd, "ON") == 0)
        {
                static const char *const list_COMMENT[] =
-               {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
+               {"CAST", "CONVERSION", "DATABASE", "FOREIGN TABLE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
                        "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
                        "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
                "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
@@ -1541,6 +1578,16 @@ psql_completion(char *text, int start, int end)
                         pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
                COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
 
+       /* CREATE FOREIGN */
+       else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+       {
+               static const char *const list_CREATE_FOREIGN[] =
+               {"DATA WRAPPER", "TABLE", NULL};
+
+               COMPLETE_WITH_LIST(list_CREATE_FOREIGN);
+       }
+
        /* CREATE FOREIGN DATA WRAPPER */
        else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
                         pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
@@ -1911,6 +1958,14 @@ psql_completion(char *text, int start, int end)
                        COMPLETE_WITH_LIST(list_DROPCR);
                }
        }
+       else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
+                        pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+       {
+               static const char *const drop_CREATE_FOREIGN[] =
+               {"DATA WRAPPER", "TABLE", NULL};
+
+               COMPLETE_WITH_LIST(drop_CREATE_FOREIGN);
+       }
        else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
                         (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
                          pg_strcasecmp(prev3_wd, "FUNCTION") == 0) &&
@@ -2015,6 +2070,12 @@ psql_completion(char *text, int start, int end)
                         pg_strcasecmp(prev_wd, "WRAPPER") == 0)
                COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
 
+/* FOREIGN TABLE */
+       else if (pg_strcasecmp(prev3_wd, "CREATE") != 0 &&
+                        pg_strcasecmp(prev2_wd, "FOREIGN") == 0 &&
+                        pg_strcasecmp(prev_wd, "TABLE") == 0)
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
+
 /* GRANT && REVOKE */
        /* Complete GRANT/REVOKE with a list of privileges */
        else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
@@ -2046,10 +2107,11 @@ psql_completion(char *text, int start, int end)
        else if ((pg_strcasecmp(prev3_wd, "GRANT") == 0 ||
                          pg_strcasecmp(prev3_wd, "REVOKE") == 0) &&
                         pg_strcasecmp(prev_wd, "ON") == 0)
-               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv,
+               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf,
                                                                   " UNION SELECT 'DATABASE'"
                                                                   " UNION SELECT 'FOREIGN DATA WRAPPER'"
                                                                   " UNION SELECT 'FOREIGN SERVER'"
+                                                                  " UNION SELECT 'FOREIGN TABLE'"
                                                                   " UNION SELECT 'FUNCTION'"
                                                                   " UNION SELECT 'LANGUAGE'"
                                                                   " UNION SELECT 'LARGE OBJECT'"
@@ -2061,7 +2123,7 @@ psql_completion(char *text, int start, int end)
                         pg_strcasecmp(prev_wd, "FOREIGN") == 0)
        {
                static const char *const list_privilege_foreign[] =
-               {"DATA WRAPPER", "SERVER", NULL};
+               {"DATA WRAPPER", "SERVER", "TABLE", NULL};
 
                COMPLETE_WITH_LIST(list_privilege_foreign);
        }
@@ -2582,7 +2644,7 @@ psql_completion(char *text, int start, int end)
        else if (pg_strcasecmp(prev_wd, "FROM") == 0 &&
                         pg_strcasecmp(prev3_wd, "COPY") != 0 &&
                         pg_strcasecmp(prev3_wd, "\\copy") != 0)
-               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf, NULL);
 
 /* Backslash commands */
 /* TODO:  \dc \dd \dl */
@@ -2620,7 +2682,7 @@ psql_completion(char *text, int start, int end)
                COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
        else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0
                         || strncmp(prev_wd, "\\z", strlen("\\z")) == 0)
-               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf, NULL);
        else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0)
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
        else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0)
@@ -2635,7 +2697,7 @@ psql_completion(char *text, int start, int end)
 
        /* must be at end of \d list */
        else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0)
-               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisv, NULL);
+               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisvf, NULL);
 
        else if (strcmp(prev_wd, "\\ef") == 0)
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
index ac13a27830b0eb4886a16d3428af5e78a632c609..8e41305691ca9c38144ce498b86867c4d19a2093 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201012291
+#define CATALOG_VERSION_NO     201101011
 
 #endif
index 5de61f1b26f48088eacaa2b8bd53e8c8c7140e60..c6ab313edf8c57691a6dce4eb2ceecbab39968c9 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 de11e440e8441736e152a569ee8404ab7d4ff1cf..a3fb916903be25efcb002cfa35207bb81f76856d 100644 (file)
@@ -275,6 +275,9 @@ DECLARE_UNIQUE_INDEX(pg_user_mapping_oid_index, 174, on pg_user_mapping using bt
 DECLARE_UNIQUE_INDEX(pg_user_mapping_user_server_index, 175, on pg_user_mapping using btree(umuser oid_ops, umserver oid_ops));
 #define UserMappingUserServerIndexId   175
 
+DECLARE_UNIQUE_INDEX(pg_foreign_table_relid_index, 3119, on pg_foreign_table using btree(ftrelid oid_ops));
+#define ForeignTableRelidIndexId 3119
+
 DECLARE_UNIQUE_INDEX(pg_default_acl_role_nsp_obj_index, 827, on pg_default_acl using btree(defaclrole oid_ops, defaclnamespace oid_ops, defaclobjtype char_ops));
 #define DefaultAclRoleNspObjIndexId 827
 DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btree(oid oid_ops));
index f0cff76e3a552b9eb9bce4676c54cb7306816cf1..b6a34c1779aeae040f163c494081ddae392419c4 100644 (file)
@@ -148,6 +148,7 @@ DESCR("");
 #define                  RELKIND_TOASTVALUE      't'           /* moved off huge values */
 #define                  RELKIND_VIEW                    'v'           /* view */
 #define                  RELKIND_COMPOSITE_TYPE  'c'           /* composite type */
+#define                  RELKIND_FOREIGN_TABLE   'f'           /* foreign table */
 
 #define                  RELPERSISTENCE_PERMANENT      'p'
 #define                  RELPERSISTENCE_UNLOGGED       'u'
diff --git a/src/include/catalog/pg_foreign_table.h b/src/include/catalog/pg_foreign_table.h
new file mode 100644 (file)
index 0000000..ada807a
--- /dev/null
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_foreign_table.h
+ *       definition of the system "foreign table" relation (pg_foreign_table)
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_foreign_table.h
+ *
+ * NOTES
+ *       the genbki.sh script reads this file and generates .bki
+ *       information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_FOREIGN_TABLE_H
+#define PG_FOREIGN_TABLE_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *             pg_foreign_table definition.  cpp turns this into
+ *             typedef struct FormData_pg_foreign_table
+ * ----------------
+ */
+#define ForeignTableRelationId 3118
+
+CATALOG(pg_foreign_table,3118) BKI_WITHOUT_OIDS
+{
+       Oid                     ftrelid;                /* OID of foreign table */
+       Oid                     ftserver;               /* OID of foreign server */
+       text            ftoptions[1];   /* FDW-specific options */
+} FormData_pg_foreign_table;
+
+/* ----------------
+ *             Form_pg_foreign_table corresponds to a pointer to a tuple with
+ *             the format of pg_foreign_table relation.
+ * ----------------
+ */
+typedef FormData_pg_foreign_table *Form_pg_foreign_table;
+
+/* ----------------
+ *             compiler constants for pg_foreign_table
+ * ----------------
+ */
+
+#define Natts_pg_foreign_table                                 3
+#define Anum_pg_foreign_table_ftrelid                  1
+#define Anum_pg_foreign_table_ftserver                 2
+#define Anum_pg_foreign_table_ftoptions                        3
+
+#endif   /* PG_FOREIGN_TABLE_H */
index 82ec446c0e551c0a710e1c20737c1b9aaa5d9343..3d3e9f959aeb41206cd6f227202e9f7de195a44a 100644 (file)
@@ -154,6 +154,11 @@ 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);
+extern Datum transformGenericOptions(Oid catalogId,
+                                                                        Datum oldOptions,
+                                                                        List *options,
+                                                                        Oid fdwvalidator);
 
 /* support routines in commands/define.c */
 
index 1ce5d81b2c0f900e801dfb7f1a6744fd36eb0d97..456e8e216aa8a2a10bdf94b2aa272cc3af2d5b89 100644 (file)
@@ -353,6 +353,7 @@ typedef enum NodeTag
        T_DropUserMappingStmt,
        T_AlterTableSpaceOptionsStmt,
        T_SecLabelStmt,
+       T_CreateForeignTableStmt,
 
        /*
         * TAGS FOR PARSE TREE NODES (parsenodes.h)
index 9225c3ad16fe11f87e808fe52e933bf3b15cabbf..3d2ae991b73ceb1ab58f0eaba7a000cc7969aa49 100644 (file)
@@ -1066,6 +1066,7 @@ typedef enum ObjectType
        OBJECT_DOMAIN,
        OBJECT_FDW,
        OBJECT_FOREIGN_SERVER,
+       OBJECT_FOREIGN_TABLE,
        OBJECT_FUNCTION,
        OBJECT_INDEX,
        OBJECT_LANGUAGE,
@@ -1165,7 +1166,8 @@ typedef enum AlterTableType
        AT_EnableReplicaRule,           /* ENABLE REPLICA RULE name */
        AT_DisableRule,                         /* DISABLE RULE name */
        AT_AddInherit,                          /* INHERIT parent */
-       AT_DropInherit                          /* NO INHERIT parent */
+       AT_DropInherit,                         /* NO INHERIT parent */
+       AT_GenericOptions,                      /* OPTIONS (...) */
 } AlterTableType;
 
 typedef struct AlterTableCmd   /* one subcommand of an ALTER TABLE */
@@ -1226,6 +1228,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 */
@@ -1579,6 +1582,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
  * ----------------------
@@ -2068,6 +2083,7 @@ typedef struct RenameStmt
 {
        NodeTag         type;
        ObjectType      renameType;             /* OBJECT_TABLE, OBJECT_COLUMN, etc */
+       ObjectType      relationType;   /* if column name, associated relation type */
        RangeVar   *relation;           /* in case it's a table */
        List       *object;                     /* in case it's some other object */
        List       *objarg;                     /* argument types, if applicable */
index d729e6f1d2cc688ec280898a45a39c231a45492c..aac74427104b18ba02ca473907bd0068bd77bf68 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 48700e8a5b882f1bec370348bb1f3f5d3355b4e3..66515a8f82a8d7f5b02794ba3636dc8a7b643887 100644 (file)
@@ -56,6 +56,7 @@ enum SysCacheIdentifier
        FOREIGNDATAWRAPPEROID,
        FOREIGNSERVERNAME,
        FOREIGNSERVEROID,
+       FOREIGNTABLEREL,
        INDEXRELID,
        LANGNAME,
        LANGOID,
index e415730bd09fb67c57963ba819353c17efec67a3..3d126bbf30835409f430fbf3670c75baff7f5084 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, 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..6f2a7a6efd66e11b9fa3ca6624fe771f99f9053c 100644 (file)
@@ -627,6 +627,107 @@ DROP SERVER s7;
  t1     | regress_test_role
 (8 rows)
 
+-- CREATE FOREIGN TABLE
+CREATE SCHEMA foreign_schema;
+CREATE SERVER sc FOREIGN DATA WRAPPER dummy;
+CREATE FOREIGN TABLE ft1 ();                                    -- ERROR
+ERROR:  syntax error at or near ";"
+LINE 1: CREATE FOREIGN TABLE ft1 ();
+                                   ^
+CREATE FOREIGN TABLE ft1 () SERVER no_server;                   -- ERROR
+ERROR:  server "no_server" does not exist
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER sc;                 -- ERROR
+NOTICE:  CREATE FOREIGN TABLE will create implicit sequence "ft1_c1_seq" for serial column "ft1.c1"
+ERROR:  default values on foreign tables are not supported
+CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;                -- ERROR
+ERROR:  syntax error at or near "WITH OIDS"
+LINE 1: CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;
+                                              ^
+CREATE FOREIGN TABLE ft1 (
+       c1 integer NOT NULL,
+       c2 text,
+       c3 date
+) SERVER sc OPTIONS (delimiter ',', quote '"');
+COMMENT ON FOREIGN TABLE ft1 IS 'ft1';
+COMMENT ON COLUMN ft1.c1 IS 'ft1.c1';
+\d+ ft1
+              Foreign table "public.ft1"
+ Column |  Type   | Modifiers | Storage  | Description 
+--------+---------+-----------+----------+-------------
+ c1     | integer | not null  | plain    | ft1.c1
+ c2     | text    |           | extended | 
+ c3     | date    |           | plain    | 
+Server: sc
+Has OIDs: no
+
+\det+
+                List of foreign tables
+ Schema | Table | Server |          Options           
+--------+-------+--------+----------------------------
+ public | ft1   | sc     | {"delimiter=,","quote=\""}
+(1 row)
+
+CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
+ERROR:  "ft1" is not a table
+-- ALTER FOREIGN TABLE
+COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
+COMMENT ON FOREIGN TABLE ft1 IS NULL;
+COMMENT ON COLUMN ft1.c1 IS 'foreign column';
+COMMENT ON COLUMN ft1.c1 IS NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0;        -- ERROR
+ERROR:  default values on foreign tables are not supported
+ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0;          -- ERROR
+ERROR:  "ft1" is not a table or view
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT;           -- ERROR
+ERROR:  "ft1" is not a table or view
+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:  ALTER TYPE USING is not supported on foreign tables
+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);
+ERROR:  "ft1" is not a table
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const;               -- ERROR
+ERROR:  "ft1" is not a table
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
+ERROR:  "ft1" is not a table
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ERROR:  "ft1" is not a table
+ALTER FOREIGN TABLE ft1 SET WITH OIDS;                          -- ERROR
+ERROR:  "ft1" is not a table
+ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
+ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@');
+ALTER FOREIGN TABLE ft1 DROP COLUMN no_column;                  -- ERROR
+ERROR:  column "no_column" of relation "ft1" does not exist
+ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column;
+NOTICE:  column "no_column" of relation "ft1" does not exist, skipping
+ALTER FOREIGN TABLE ft1 DROP COLUMN c9;
+ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema;
+ALTER FOREIGN TABLE ft1 SET TABLESPACE ts;                      -- ERROR
+ERROR:  relation "ft1" does not exist
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
+\d foreign_schema.foreign_table_1
+Foreign table "foreign_schema.foreign_table_1"
+      Column      |  Type   | Modifiers 
+------------------+---------+-----------
+ foreign_column_1 | integer | not null
+ c2               | text    | 
+ c3               | date    | 
+ c4               | integer | 
+ c6               | integer | not null
+ c7               | integer | 
+ c8               | text    | 
+ c10              | integer | 
+Server: sc
+
 -- Information schema
 SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
  foreign_data_wrapper_catalog | foreign_data_wrapper_name | authorization_identifier | library_name | foreign_data_wrapper_language 
@@ -649,9 +750,10 @@ SELECT * FROM information_schema.foreign_servers ORDER BY 1, 2;
  regression             | s5                  | regression                   | foo                       |                     | 15.0                   | regress_test_role
  regression             | s6                  | regression                   | foo                       |                     | 16.0                   | regress_test_indirect
  regression             | s8                  | regression                   | postgresql                |                     |                        | foreign_data_user
+ regression             | sc                  | regression                   | dummy                     |                     |                        | foreign_data_user
  regression             | t1                  | regression                   | foo                       |                     |                        | regress_test_indirect
  regression             | t2                  | regression                   | foo                       |                     |                        | regress_test_role
-(6 rows)
+(7 rows)
 
 SELECT * FROM information_schema.foreign_server_options ORDER BY 1, 2, 3;
  foreign_server_catalog | foreign_server_name |   option_name   | option_value 
@@ -707,6 +809,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            | foreign_schema       | foreign_table_1    | regression             | sc
+(1 row)
+
+SELECT * FROM information_schema.foreign_table_options ORDER BY 1, 2, 3, 4;
+ foreign_table_catalog | foreign_table_schema | foreign_table_name | option_name | option_value 
+-----------------------+----------------------+--------------------+-------------+--------------
+ regression            | foreign_schema       | foreign_table_1    | escape      | @
+ regression            | foreign_schema       | foreign_table_1    | quote       | ~
+(2 rows)
+
 SET ROLE regress_test_role;
 SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4;
  authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value 
@@ -938,7 +1053,14 @@ CREATE USER MAPPING FOR current_user SERVER s9;
 DROP SERVER s9 CASCADE;                                         -- ERROR
 ERROR:  must be owner of foreign server s9
 RESET ROLE;
+-- 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;
 -- Cleanup
+DROP SCHEMA foreign_schema CASCADE;
 DROP ROLE regress_test_role;                                -- ERROR
 ERROR:  role "regress_test_role" cannot be dropped because some objects depend on it
 DETAIL:  privileges for server s4
@@ -974,6 +1096,7 @@ DROP ROLE unprivileged_role;
 DROP ROLE regress_test_role2;
 DROP FOREIGN DATA WRAPPER postgresql CASCADE;
 DROP FOREIGN DATA WRAPPER dummy CASCADE;
+NOTICE:  drop cascades to server sc
 \c
 DROP ROLE foreign_data_user;
 -- At this point we should have no wrappers, no servers, and no mappings.
index cf714b2cfce19b3dac036e4e24fdde4048482a8d..22e4c99fe666d04aac55c99261ddf7a4f5769711 100644 (file)
@@ -1276,8 +1276,8 @@ drop table cchild;
 -- Check that ruleutils are working
 --
 SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname;
-          viewname           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                definition                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+          viewname           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              definition                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  iexit                       | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
  pg_cursors                  | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
  pg_group                    | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
@@ -1287,7 +1287,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
  pg_prepared_xacts           | SELECT p.transaction, p.gid, p.prepared, u.rolname AS owner, d.datname AS database FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid)));
  pg_roles                    | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolreplication, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, s.setconfig AS rolconfig, pg_authid.oid FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))));
  pg_rules                    | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name);
- pg_seclabels                | (((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0);
+ pg_seclabels                | (((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0);
  pg_settings                 | SELECT a.name, a.setting, a.unit, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val, a.enumvals, a.boot_val, a.reset_val, a.sourcefile, a.sourceline FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline);
  pg_shadow                   | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolreplication AS userepl, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin;
  pg_stat_activity            | SELECT s.datid, d.datname, s.procpid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_port, s.backend_start, s.xact_start, s.query_start, s.waiting, s.current_query FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, application_name, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
index 9596b0b712b71e1eaba89a148eea9c8f1f33c6a6..1ee820fd7c9400103adc436ca5c36304a6cc181d 100644 (file)
@@ -102,6 +102,7 @@ SELECT relname, relhasindex
  pg_enum                 | t
  pg_foreign_data_wrapper | t
  pg_foreign_server       | t
+ pg_foreign_table        | t
  pg_index                | t
  pg_inherits             | t
  pg_language             | t
@@ -154,7 +155,7 @@ SELECT relname, relhasindex
  timetz_tbl              | f
  tinterval_tbl           | f
  varchar_tbl             | f
-(143 rows)
+(144 rows)
 
 --
 -- another sanity check: every system catalog that has OIDs should have
index 556672d408a9ca2b27747fe787e4bdaed01899a1..e30ecbc6feba07d49bec5c27204b45418c096ccf 100644 (file)
@@ -278,7 +278,7 @@ WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT
 -- Look for illegal values in pg_class fields
 SELECT p1.oid, p1.relname
 FROM pg_class as p1
-WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v');
+WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v', 'f');
  oid | relname 
 -----+---------
 (0 rows)
index d89b26d1fc6d007a95224d9c9638b8543c0a524a..655ddc0d0c4b086ef620cabaec5863992f9d28e5 100644 (file)
@@ -254,6 +254,61 @@ RESET ROLE;
 DROP SERVER s7;
 \deu
 
+-- CREATE FOREIGN TABLE
+CREATE SCHEMA foreign_schema;
+CREATE SERVER sc FOREIGN DATA WRAPPER dummy;
+CREATE FOREIGN TABLE ft1 ();                                    -- ERROR
+CREATE FOREIGN TABLE ft1 () SERVER no_server;                   -- ERROR
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER sc;                 -- ERROR
+CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;                -- ERROR
+CREATE FOREIGN TABLE ft1 (
+       c1 integer NOT NULL,
+       c2 text,
+       c3 date
+) SERVER sc OPTIONS (delimiter ',', quote '"');
+COMMENT ON FOREIGN TABLE ft1 IS 'ft1';
+COMMENT ON COLUMN ft1.c1 IS 'ft1.c1';
+\d+ ft1
+\det+
+CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
+
+-- ALTER FOREIGN TABLE
+COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
+COMMENT ON FOREIGN TABLE ft1 IS NULL;
+COMMENT ON COLUMN ft1.c1 IS 'foreign column';
+COMMENT ON COLUMN ft1.c1 IS NULL;
+
+ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0;        -- ERROR
+ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
+
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0;          -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT;           -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const;               -- ERROR
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ALTER FOREIGN TABLE ft1 SET WITH OIDS;                          -- ERROR
+ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
+ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@');
+ALTER FOREIGN TABLE ft1 DROP COLUMN no_column;                  -- ERROR
+ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column;
+ALTER FOREIGN TABLE ft1 DROP COLUMN c9;
+ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema;
+ALTER FOREIGN TABLE ft1 SET TABLESPACE ts;                      -- ERROR
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
+\d foreign_schema.foreign_table_1
+
 -- Information schema
 
 SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
@@ -264,6 +319,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;
@@ -366,7 +423,13 @@ CREATE USER MAPPING FOR current_user SERVER s9;
 DROP SERVER s9 CASCADE;                                         -- ERROR
 RESET ROLE;
 
+-- DROP FOREIGN TABLE
+DROP FOREIGN TABLE no_table;                                    -- ERROR
+DROP FOREIGN TABLE IF EXISTS no_table;
+DROP FOREIGN TABLE foreign_schema.foreign_table_1;
+
 -- Cleanup
+DROP SCHEMA foreign_schema CASCADE;
 DROP ROLE regress_test_role;                                -- ERROR
 DROP SERVER s5 CASCADE;
 DROP SERVER t1 CASCADE;
index af7aa2d8b3da24b91fabfb6a7a04f081341f1d58..fa6dd75f07fd4066f12fad76fb651c07b9c35e9d 100644 (file)
@@ -217,7 +217,7 @@ WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT
 
 SELECT p1.oid, p1.relname
 FROM pg_class as p1
-WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v');
+WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 'c', 't', 'v', 'f');
 
 -- Indexes should have an access method, others not.