<entry>foreign server definitions</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-foreign-table"><structname>pg_foreign_table</structname></link></entry>
+ <entry>additional foreign table information</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-index"><structname>pg_index</structname></link></entry>
<entry>additional index information</entry>
<entry>
<literal>r</> = ordinary table, <literal>i</> = index,
<literal>S</> = sequence, <literal>v</> = view, <literal>c</> =
- composite type, <literal>t</> = TOAST
- table
+ composite type, <literal>t</> = TOAST table,
+ <literal>f</> = foiregn table
</entry>
</row>
</sect1>
+ <sect1 id="catalog-pg-foreign-table">
+ <title><structname>pg_foreign_table</structname></title>
+
+ <indexterm zone="catalog-pg-foreign-table">
+ <primary>pg_foreign_table</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_foreign_table</structname> contains part
+ of the information about foreign tables.
+ The rest is mostly in <structname>pg_class</structname>.
+ </para>
+
+ <table>
+ <title><structname>pg_foreign_table</> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>ftrelid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+ <entry>The OID of the <structname>pg_class</> entry for this foreign table</entry>
+ </row>
+
+ <row>
+ <entry><structfield>ftserver</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-foreign-server"><structname>pg_foreign_server</structname></link>.oid</literal></entry>
+ <entry>The OID of the foreign server for this foreign table</entry>
+ </row>
+
+ <row>
+ <entry><structfield>srvoptions</structfield></entry>
+ <entry><type>text[]</type></entry>
+ <entry></entry>
+ <entry>
+ Foreign table specific options, as <quote>keyword=value</> strings.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
+
<sect1 id="catalog-pg-index">
<title><structname>pg_index</structname></title>
</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>
<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>
<!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">
<!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">
<!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">
<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>
--- /dev/null
+<!--
+$PostgreSQL$
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERFOREIGNTABLE">
+ <refmeta>
+ <refentrytitle>ALTER FOREIGN TABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER FOREIGN TABLE</refname>
+ <refpurpose>change the definition of a foreign table</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterforeigntable">
+ <primary>ALTER FOREIGN TABLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable> [ * ]
+ <replaceable class="PARAMETER">action</replaceable> [, ... ]
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable> [ * ]
+ RENAME [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> TO <replaceable class="PARAMETER">new_column</replaceable>
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
+ RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
+ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
+ SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
+
+<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
+
+ ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+ DROP [ COLUMN ] [ IF EXISTS ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
+ ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">type</replaceable>
+ ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET DEFAULT <replaceable class="PARAMETER">expression</replaceable>
+ ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
+ ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
+ ADD <replaceable class="PARAMETER">table_constraint</replaceable>
+ DROP CONSTRAINT [ IF EXISTS ] <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
+ DISABLE RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
+ ENABLE RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
+ ENABLE REPLICA RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
+ ENABLE ALWAYS RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
+ SET WITH OIDS
+ SET WITHOUT OIDS
+ INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
+ NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
+ OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
+ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>ALTER FOREIGN TABLE</command> changes the definition of an existing table.
+ There are several subforms:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>ADD COLUMN</literal></term>
+ <listitem>
+ <para>
+ This form adds a new column to the foreign table, using the same syntax as
+ <xref linkend="SQL-CREATETABLE">.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DROP COLUMN [ IF EXISTS ]</literal></term>
+ <listitem>
+ <para>
+ This form drops a column from a foreign table.
+ Table constraints involving the column will be automatically
+ dropped as well. You will need to say <literal>CASCADE</> if
+ anything outside the table depends on the column, for example,
+ views.
+ If <literal>IF EXISTS</literal> is specified and the column
+ does not exist, no error is thrown. In this case a notice
+ is issued instead.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET DATA TYPE</literal></term>
+ <listitem>
+ <para>
+ This form changes the type of a column of a foreign table.
+ Simple table constraints involving the column will be automatically
+ converted to use the new column type by reparsing the originally
+ supplied expression. The optional <literal>USING</literal>
+ clause specifies how to compute the new column value from the old;
+ if omitted, the default conversion is the same as an assignment
+ cast from old data type to new. A <literal>USING</literal>
+ clause must be provided if there is no implicit or assignment
+ cast from old to new type.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET</literal>/<literal>DROP NOT NULL</literal></term>
+ <listitem>
+ <para>
+ These forms change whether a column is marked to allow null
+ values or to reject null values. You can only use <literal>SET
+ NOT NULL</> when the column contains no null values.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable></literal></term>
+ <listitem>
+ <para>
+ This form adds a new constraint to a foreign table using the same syntax as
+ <xref linkend="SQL-CREATETABLE">.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DROP CONSTRAINT [ IF EXISTS ]</literal></term>
+ <listitem>
+ <para>
+ This form drops the specified constraint on a foreign table.
+ If <literal>IF EXISTS</literal> is specified and the constraint
+ does not exist, no error is thrown. In this case a notice is issued instead.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DISABLE</literal>/<literal>ENABLE [ REPLICA | ALWAYS ] RULE</literal></term>
+ <listitem>
+ <para>
+ These forms configure the firing of rewrite rules belonging to the table.
+ A disabled rule is still known to the system, but is not applied
+ during query rewriting.
+ This configuration is ignored for <literal>ON SELECT</literal> rules, which
+ are always applied in order to keep views working even if the current
+ session is in a non-default replication role.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET WITH OIDS</literal></term>
+ <listitem>
+ <para>
+ This form adds an <literal>oid</literal> system column to the
+ table (see <xref linkend="ddl-system-columns">).
+ It does nothing if the table already has OIDs.
+ </para>
+
+ <para>
+ Note that this is not equivalent to <literal>ADD COLUMN oid oid</>;
+ that would add a normal column that happened to be named
+ <literal>oid</>, not a system column.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET WITHOUT OIDS</literal></term>
+ <listitem>
+ <para>
+ This form removes the <literal>oid</literal> system column from the
+ table. This is exactly equivalent to
+ <literal>DROP COLUMN oid RESTRICT</literal>,
+ except that it will not complain if there is already no
+ <literal>oid</literal> column.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term>
+ <listitem>
+ <para>
+ This form adds the target table as a new child of the specified parent
+ table. Subsequently, queries against the parent will include records
+ of the target table. To be added as a child, the target table must
+ already contain all the same columns as the parent (it could have
+ additional columns, too). The columns must have matching data types,
+ and if they have <literal>NOT NULL</literal> constraints in the parent
+ then they must also have <literal>NOT NULL</literal> constraints in the
+ child.
+ </para>
+
+ <para>
+ There must also be matching child-table constraints for all
+ <literal>CHECK</literal> constraints of the parent.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term>
+ <listitem>
+ <para>
+ This form removes the target table from the list of children of the
+ specified parent table.
+ Queries against the parent table will no longer include records drawn
+ from the target table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>OWNER</literal></term>
+ <listitem>
+ <para>
+ This form changes the owner of the foreign table to the
+ specified user.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RENAME</literal></term>
+ <listitem>
+ <para>
+ The <literal>RENAME</literal> forms change the name of a foreign table
+ or the name of an individual column in
+ a foreign table. There is no effect on the stored data.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET SCHEMA</literal></term>
+ <listitem>
+ <para>
+ This form moves the foreign table into another schema. Associated
+ constraints owned by table columns are moved as well.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
+ <listitem>
+ <para>
+ Change options for the foreign table.
+ <literal>ADD</>, <literal>SET</>, and <literal>DROP</>
+ specify the action to be performed. <literal>ADD</> is assumed
+ if no operation is explicitly specified. Option names must be
+ unique; names and values are also validated using the foreign
+ data wrapper library.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </para>
+
+ <para>
+ All the actions except <literal>RENAME</literal> and <literal>SET SCHEMA</>
+ can be combined into
+ a list of multiple alterations to apply in parallel. For example, it
+ is possible to add several columns and/or alter the type of several
+ columns in a single command. This is particularly useful with large
+ foreign tables, since only one pass over the table need be made.
+ </para>
+
+ <para>
+ You must own the table to use <command>ALTER FOREIGN TABLE</>.
+ To change the schema of a foreign table, you must also have
+ <literal>CREATE</literal> privilege on the new schema.
+ To add the table as a new child of a parent table, you must own the
+ parent table as well.
+ To alter the owner, you must also be a direct or indirect member of the new
+ owning role, and that role must have <literal>CREATE</literal> privilege on
+ the table's schema. (These restrictions enforce that altering the owner
+ doesn't do anything you couldn't do by dropping and recreating the table.
+ However, a superuser can alter ownership of any table anyway.)
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (possibly schema-qualified) of an existing table to
+ alter. If <literal>ONLY</> is specified, only that table is
+ altered. If <literal>ONLY</> is not specified, the table and any
+ descendant foreign tables are altered.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">column</replaceable></term>
+ <listitem>
+ <para>
+ Name of a new or existing column.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">new_column</replaceable></term>
+ <listitem>
+ <para>
+ New name for an existing column.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">new_name</replaceable></term>
+ <listitem>
+ <para>
+ New name for the table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">type</replaceable></term>
+ <listitem>
+ <para>
+ Data type of the new column, or new data type for an existing
+ column.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">table_constraint</replaceable></term>
+ <listitem>
+ <para>
+ New table constraint for the table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">constraint_name</replaceable></term>
+ <listitem>
+ <para>
+ Name of an existing constraint to drop.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>CASCADE</literal></term>
+ <listitem>
+ <para>
+ Automatically drop objects that depend on the dropped column
+ or constraint (for example, views referencing the column).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Refuse to drop the column or constraint if there are any dependent
+ objects. This is the default behavior.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">storage_parameter</replaceable></term>
+ <listitem>
+ <para>
+ The name of a foreign table storage parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">value</replaceable></term>
+ <listitem>
+ <para>
+ The new value for a foreign table storage parameter.
+ This might be a number or a word depending on the parameter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">parent_table</replaceable></term>
+ <listitem>
+ <para>
+ A parent table to associate or de-associate with this table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">new_owner</replaceable></term>
+ <listitem>
+ <para>
+ The user name of the new owner of the table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">new_schema</replaceable></term>
+ <listitem>
+ <para>
+ The name of the schema to which the table will be moved.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ The key word <literal>COLUMN</literal> is noise and can be omitted.
+ </para>
+
+ <para>
+ Consistency with the foreign server is not checked even when a column is
+ added or removed with <literal>ADD COLUMN</literal> or
+ <literal>DROP COLUMN</literal>, a system <literal>oid</> column is added
+ or removed, a <literal>CHECK</> or <literal>NOT NULL</> constraint is
+ added, or column type is changed with <literal>ALTER TYPE</>.
+ </para>
+
+ <para>
+ Refer to <xref linkend="sql-createforeigntable"> for a further description of valid
+ parameters. <xref linkend="ddl"> has further information on
+ inheritance.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To add a column of type <type>varchar</type> to a foreign table:
+<programlisting>
+ALTER FOREIGN TABLE distributors ADD COLUMN address varchar(30);
+</programlisting>
+ </para>
+
+ <para>
+ To drop a column from a foreign table:
+<programlisting>
+ALTER FOREIGN TABLE distributors DROP COLUMN address RESTRICT;
+</programlisting>
+ </para>
+
+ <para>
+ To change the types of two existing columns in one operation:
+<programlisting>
+ALTER FOREIGN TABLE distributors
+ ALTER COLUMN address TYPE varchar(80),
+ ALTER COLUMN name TYPE varchar(100);
+</programlisting>
+ </para>
+
+ <para>
+ To rename an existing column:
+<programlisting>
+ALTER FOREIGN TABLE distributors RENAME COLUMN address TO city;
+</programlisting>
+ </para>
+
+ <para>
+ To rename an existing table:
+<programlisting>
+ALTER FOREIGN TABLE distributors RENAME TO suppliers;
+</programlisting>
+ </para>
+
+ <para>
+ To add a not-null constraint to a column:
+<programlisting>
+ALTER FOREIGN TABLE distributors ALTER COLUMN street SET NOT NULL;
+</programlisting>
+ To remove a not-null constraint from a column:
+<programlisting>
+ALTER FOREIGN TABLE distributors ALTER COLUMN street DROP NOT NULL;
+</programlisting>
+ </para>
+
+ <para>
+ To add a check constraint to a foreign table:
+<programlisting>
+ALTER FOREIGN TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
+</programlisting>
+ </para>
+
+ <para>
+ To remove a check constraint from a foreign table and all its children:
+<programlisting>
+ALTER FOREIGN TABLE distributors DROP CONSTRAINT zipchk;
+</programlisting>
+ </para>
+
+ <para>
+ To remove a check constraint from a foreign table only:
+<programlisting>
+ALTER FOREIGN TABLE ONLY distributors DROP CONSTRAINT zipchk;
+</programlisting>
+ </para>
+
+ <para>
+ To move a foreign table to a different schema:
+<programlisting>
+ALTER FOREIGN TABLE myschema.distributors SET SCHEMA yourschema;
+</programlisting>
+ </para>
+
+ <para>
+ To chage options of a foreign table:
+<programlisting>
+ALTER FOREIGN TABLE myschema.distributors OPTIONS (ADD opt1 'value', SET opt2, 'value2', DROP opt3 'value3');
+</programlisting>
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ The forms <literal>ADD</literal>, <literal>DROP</>,
+ and <literal>SET DATA TYPE</literal>
+ conform with the SQL standard. The other forms are
+ <productname>PostgreSQL</productname> extensions of the SQL standard.
+ Also, the ability to specify more than one manipulation in a single
+ <command>ALTER FOREIGN TABLE</> command is an extension.
+ </para>
+
+ <para>
+ <command>ALTER FOREIGN TABLE DROP COLUMN</> can be used to drop the only
+ column of a foreign table, leaving a zero-column table. This is an
+ extension of SQL, which disallows zero-column foreign tables.
+ </para>
+ </refsect1>
+</refentry>
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> |
COMMENT ON CONVERSION my_conv IS 'Conversion to UTF8';
COMMENT ON DATABASE my_database IS 'Development Database';
COMMENT ON DOMAIN my_domain IS 'Email Address Domain';
+COMMENT ON FOREIGN TABLE my_schema.my_foreign_table IS 'Employee Information in other database';
COMMENT ON FUNCTION my_function (timestamp) IS 'Returns Roman Numeral';
COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee ID';
COMMENT ON LANGUAGE plpython IS 'Python support for stored procedures';
--- /dev/null
+<!--
+$PostgreSQL$
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATEFOREIGNTABLE">
+ <refmeta>
+ <refentrytitle>CREATE FOREIGN TABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE FOREIGN TABLE</refname>
+ <refpurpose>define a new foreign table</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-createforeigntable">
+ <primary>CREATE FOREIGN TABLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE FOREIGN TABLE <replaceable class="PARAMETER">table_name</replaceable> ( [
+ { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+ | <replaceable>table_constraint</replaceable>
+ | LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
+ [, ... ]
+] )
+[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
+ SERVER <replaceable class="parameter">server_name</replaceable>
+[ WITH OIDS | WITHOUT OIDS ]
+[ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
+
+CREATE FOREIGN TABLE <replaceable class="PARAMETER">table_name</replaceable>
+ OF <replaceable class="PARAMETER">type_name</replaceable> [ (
+ { <replaceable class="PARAMETER">column_name</replaceable> WITH OPTIONS [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+ | <replaceable>table_constraint</replaceable> }
+ [, ... ]
+) ]
+[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
+ SERVER <replaceable class="parameter">server_name</replaceable>
+[ WITH OIDS | WITHOUT OIDS ]
+[ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
+
+<phrase>where <replaceable class="PARAMETER">column_constraint</replaceable> is:</phrase>
+
+[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
+{ NOT NULL |
+ NULL |
+ CHECK ( <replaceable class="PARAMETER">expression</replaceable> )
+}
+
+<phrase>and <replaceable class="PARAMETER">table_constraint</replaceable> is:</phrase>
+
+[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
+CHECK ( <replaceable class="PARAMETER">expression</replaceable> )
+
+<phrase>and <replaceable class="PARAMETER">like_option</replaceable> is:</phrase>
+
+{ INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | COMMENTS | ALL }
+</synopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1 id="SQL-CREATEFOREIGNTABLE-description">
+ <title>Description</title>
+
+ <para>
+ <command>CREATE FOREIGN TABLE</command> will create a new foreign table
+ in the current database. The table will be owned by the user issuing the
+ command.
+ </para>
+
+ <para>
+ If a schema name is given (for example, <literal>CREATE FOREIGN TABLE
+ myschema.mytable ...</>) then the table is created in the specified
+ schema. Otherwise it is created in the current schema.
+ The name of the foreign table must be
+ distinct from the name of any other foreign table, table, sequence, index,
+ or view in the same schema.
+ </para>
+
+ <para>
+ <command>CREATE FOREIGN TABLE</command> also automatically creates a data
+ type that represents the composite type corresponding to one row of
+ the foreign table. Therefore, foreign tables cannot have the same
+ name as any existing data type in the same schema.
+ </para>
+
+ <para>
+ The optional constraint clauses specify constraints (tests) that
+ retrieved rows must satisfy for an select operation
+ to succeed. A constraint is an SQL object that helps define the
+ set of valid values in the table in various ways.
+ </para>
+
+ <para>
+ There are two ways to define constraints: table constraints and
+ column constraints. A column constraint is defined as part of a
+ column definition. A table constraint definition is not tied to a
+ particular column, and it can encompass more than one column.
+ Every column constraint can also be written as a table constraint;
+ a column constraint is only a notational convenience for use when the
+ constraint only affects one column.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">table_name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the table to be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>OF <replaceable class="PARAMETER">type_name</replaceable></literal></term>
+ <listitem>
+ <para>
+ Creates a <firstterm>typed table</firstterm>, which takes its
+ structure from the specified composite type (name optionally
+ schema-qualified). A typed table is tied to its type; for
+ example the table will be dropped if the type is dropped
+ (with <literal>DROP TYPE ... CASCADE</literal>).
+ </para>
+
+ <para>
+ When a typed table is created, then the data types of the
+ columns are determined by the underlying composite type and are
+ not specified by the <literal>CREATE FOREIGN TABLE</literal> command.
+ But the <literal>CREATE FOREIGN TABLE</literal> command can add defaults
+ and constraints to the table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">column_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of a column to be created in the new table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">data_type</replaceable></term>
+ <listitem>
+ <para>
+ The data type of the column. This can include array
+ specifiers. For more information on the data types supported by
+ <productname>PostgreSQL</productname>, refer to <xref
+ linkend="datatype">.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>INHERITS ( <replaceable>parent_table</replaceable> [, ... ] )</literal></term>
+ <listitem>
+ <para>
+ The optional <literal>INHERITS</> clause specifies a list of
+ tables from which the new table automatically inherits all
+ columns.
+ </para>
+
+ <para>
+ Use of <literal>INHERITS</> creates a persistent relationship
+ between the new child table and its parent table(s). Schema
+ modifications to the parent(s) normally propagate to children
+ as well, and by default the data of the child table is included in
+ scans of the parent(s).
+ </para>
+
+ <para>
+ If the same column name exists in more than one parent
+ table, an error is reported unless the data types of the columns
+ match in each of the parent tables. If there is no conflict,
+ then the duplicate columns are merged to form a single column in
+ the new table. If the column name list of the new table
+ contains a column name that is also inherited, the data type must
+ likewise match the inherited column(s), and the column
+ definitions are merged into one. If the
+ new table explicitly specifies a default value for the column,
+ this default overrides any defaults from inherited declarations
+ of the column. Otherwise, any parents that specify default
+ values for the column must all specify the same default, or an
+ error will be reported.
+ </para>
+
+ <para>
+ <literal>CHECK</> constraints are merged in essentially the same way as
+ columns: if multiple parent tables and/or the new table definition
+ contain identically-named <literal>CHECK</> constraints, these
+ constraints must all have the same check expression, or an error will be
+ reported. Constraints having the same name and expression will
+ be merged into one copy. Notice that an unnamed <literal>CHECK</>
+ constraint in the new table will never be merged, since a unique name
+ will always be chosen for it.
+ </para>
+
+ <para>
+ Column <literal>STORAGE</> settings are also copied from parent tables.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ]</literal></term>
+ <listitem>
+ <para>
+ The <literal>LIKE</literal> clause specifies a table from which
+ the new table automatically copies all column names, their data types,
+ and their not-null constraints.
+ </para>
+ <para>
+ Unlike <literal>INHERITS</literal>, the new table and original table
+ are completely decoupled after creation is complete. Changes to the
+ original table will not be applied to the new table, and it is not
+ possible to include data of the new table in scans of the original
+ table.
+ </para>
+ <para>
+ Default expressions for the copied column definitions will only be
+ copied if <literal>INCLUDING DEFAULTS</literal> is specified. The
+ default behavior is to exclude default expressions, resulting in the
+ copied columns in the new table having null defaults.
+ </para>
+ <para>
+ Not-null constraints are always copied to the new table.
+ <literal>CHECK</literal> constraints will only be copied if
+ <literal>INCLUDING CONSTRAINTS</literal> is specified; other types of
+ constraints will never be copied. Also, no distinction is made between
+ column constraints and table constraints — when constraints are
+ requested, all check constraints are copied.
+ </para>
+ <para>
+ Comments for the copied columns and constraints
+ will only be copied if <literal>INCLUDING COMMENTS</literal>
+ is specified. The default behavior is to exclude comments, resulting in
+ the copied columns and constraints in the new table having no comments.
+ </para>
+ <para>
+ <literal>INCLUDING ALL</literal> is an abbreviated form of
+ <literal>INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING COMMENTS</literal>.
+ </para>
+ <para>
+ Note also that unlike <literal>INHERITS</literal>, columns and
+ constraints copied by <literal>LIKE</> are not merged with similarly
+ named columns and constraints.
+ If the same name is specified explicitly or in another
+ <literal>LIKE</literal> clause, an error is signalled.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable></literal></term>
+ <listitem>
+ <para>
+ An optional name for a column or table constraint. If the
+ constraint is violated, the constraint name is present in error messages,
+ so constraint names like <literal>col must be positive</> can be used
+ to communicate helpful constraint information to client applications.
+ (Double-quotes are needed to specify constraint names that contain spaces.)
+ If a constraint name is not specified, the system generates a name.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>NOT NULL</></term>
+ <listitem>
+ <para>
+ The column is not allowed to contain null values.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>NULL</></term>
+ <listitem>
+ <para>
+ The column is allowed to contain null values. This is the default.
+ </para>
+
+ <para>
+ This clause is only provided for compatibility with
+ non-standard SQL databases. Its use is discouraged in new
+ applications.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>CHECK ( <replaceable class="PARAMETER">expression</replaceable> )</literal></term>
+ <listitem>
+ <para>
+ The <literal>CHECK</> clause specifies an expression producing a
+ Boolean result which new or updated rows must satisfy for an
+ insert or update operation to succeed. Expressions evaluating
+ to TRUE or UNKNOWN succeed. Should any row of an insert or
+ update operation produce a FALSE result an error exception is
+ raised and the insert or update does not alter the database. A
+ check constraint specified as a column constraint should
+ reference that column's value only, while an expression
+ appearing in a table constraint can reference multiple columns.
+ </para>
+
+ <para>
+ Currently, <literal>CHECK</literal> expressions cannot contain
+ subqueries nor refer to variables other than columns of the
+ current row.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>WITH OIDS | WITHOUT OIDS</literal></term>
+ <listitem>
+ <para>
+ This clause specifies whether rows of the foreign table should have
+ OIDs (object identifiers) assigned to them, or not.
+ If <literal>WITH OIDS</> is not specified, the default setting depends
+ upon the <xref linkend="guc-default-with-oids"> configuration parameter.
+ (If the new foreign table inherits from any tables that have OIDs, then
+ <literal>WITH OIDS</> is forced even if the command says
+ <literal>WITHOUT OIDS</>.)
+ </para>
+
+ <para>
+ If <literal>WITH OIDS</literal> is specified or implied, the first
+ column of the rows which was retrieved from the foreign table
+ must be the oid column.
+ </para>
+
+ <para>
+ To remove OIDs from a foreign table after it has been created, use <xref
+ linkend="sql-alterforeigntable">.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+
+ <refsect1 id="SQL-CREATEFOREIGNTABLE-examples">
+ <title>Examples</title>
+
+ <para>
+ Create foreign table <structname>films</> with <structname>film_server</>:
+
+<programlisting>
+CREATE FOREIGN TABLE films (
+ code char(5) NOT NULL,
+ title varchar(40) NOT NULL,
+ did integer NOT NULL,
+ date_prod date,
+ kind varchar(10),
+ len interval hour to minute
+)
+SERVER film_server;
+</programlisting>
+ </para>
+
+ <para>
+ Define a check column constraint:
+
+<programlisting>
+CREATE FOREIGN TABLE distributors (
+ did integer CHECK (did > 100),
+ name varchar(40)
+)
+SERVER distributor_server;
+</programlisting>
+ </para>
+
+ <para>
+ Define a check table constraint:
+
+<programlisting>
+CREATE FOREIGN TABLE distributors (
+ did integer,
+ name varchar(40)
+ CONSTRAINT con1 CHECK (did > 100 AND name <> '')
+)
+SERVER distributor_server;
+</programlisting>
+ </para>
+
+ <para>
+ Create a composite type and a typed foreign table:
+<programlisting>
+CREATE TYPE employee_type AS (name text, salary numeric);
+
+CREATE FOREIGN TABLE employees OF employee_type (
+ salary WITH OPTIONS CHECK (salary > 0)
+)
+SERVER employee_server;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1 id="SQL-CREATEFOREIGNTABLE-compatibility">
+ <title id="SQL-CREATEFOREIGNTABLE-compatibility-title">Compatibility</title>
+
+ <para>
+ The <command>CREATE FOREIGN TABLE</command> command conforms to the
+ <acronym>SQL</acronym> standard, with exceptions listed below.
+ </para>
+
+ <refsect2>
+ <title>Column generic options</title>
+
+ <para>
+ The SQL standard says that column definition can contain
+ column generic options, but currently column generic options are
+ not supported.
+ </para>
+ </refsect2>
+
+ <refsect2>
+ <title>Column Check Constraints</title>
+
+ <para>
+ The SQL standard says that <literal>CHECK</> column constraints
+ can only refer to the column they apply to; only <literal>CHECK</>
+ table constraints can refer to multiple columns.
+ <productname>PostgreSQL</productname> does not enforce this
+ restriction; it treats column and table check constraints alike.
+ </para>
+ </refsect2>
+
+ <refsect2>
+ <title><literal>NULL</literal> <quote>Constraint</quote></title>
+
+ <para>
+ The <literal>NULL</> <quote>constraint</quote> (actually a
+ non-constraint) is a <productname>PostgreSQL</productname>
+ extension to the SQL standard that is included for compatibility with some
+ other database systems (and for symmetry with the <literal>NOT
+ NULL</literal> constraint). Since it is the default for any
+ column, its presence is simply noise.
+ </para>
+ </refsect2>
+
+ <refsect2>
+ <title>Inheritance</title>
+
+ <para>
+ Multiple inheritance via the <literal>INHERITS</literal> clause is
+ a <productname>PostgreSQL</productname> language extension.
+ SQL:1999 and later define single inheritance using a
+ different syntax and different semantics. SQL:1999-style
+ inheritance is not yet supported by
+ <productname>PostgreSQL</productname>.
+ </para>
+ </refsect2>
+
+ <refsect2>
+ <title>Zero-column tables</title>
+
+ <para>
+ <productname>PostgreSQL</productname> allows a table of no columns
+ to be created (for example, <literal>CREATE FOREIGN TABLE foo();</>). This
+ is an extension from the SQL standard, which does not allow zero-column
+ tables. Zero-column tables are not in themselves very useful, but
+ disallowing them creates odd special cases for <command>ALTER TABLE
+ DROP COLUMN</>, so it seems cleaner to ignore this spec restriction.
+ </para>
+ </refsect2>
+
+ <refsect2>
+ <title><literal>WITH</> clause</title>
+
+ <para>
+ The <literal>WITH</> clause is a <productname>PostgreSQL</productname>
+ extension; neither storage parameters nor OIDs are in the standard.
+ </para>
+ </refsect2>
+
+ <refsect2>
+ <title>Typed Tables</title>
+
+ <para>
+ Typed tables implement a subset of the SQL standard. According to
+ the standard, a typed table has columns corresponding to the
+ underlying composite type as well as one other column that is
+ the <quote>self-referencing column</quote>. PostgreSQL does not
+ support these self-referencing columns explicitly, but the same
+ effect can be had using the OID feature.
+ </para>
+ </refsect2>
+ </refsect1>
+
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-alterforeigntable"></member>
+ <member><xref linkend="sql-dropforeigntable"></member>
+ <member><xref linkend="sql-createtable"></member>
+ <member><xref linkend="sql-createtype"></member>
+ <member><xref linkend="sql-createserver"></member>
+ </simplelist>
+ </refsect1>
+</refentry>
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>
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>
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>
--- /dev/null
+<!--
+$PostgreSQL$
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DROPFOREIGNTABLE">
+ <refmeta>
+ <refentrytitle>DROP FOREIGN TABLE</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP FOREIGN TABLE</refname>
+ <refpurpose>remove a foreign table</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-dropforeigntable">
+ <primary>DROP FOREIGN TABLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP FOREIGN TABLE</command> removes foreign tables from the database.
+ Only its owner can drop a foreign table.
+ </para>
+
+ <para>
+ <command>DROP FOREIGN TABLE</command> always removes any rules
+ and constraints that exist for the target table.
+ However, to drop a foreign table that is referenced by a view,
+ <literal>CASCADE</> must be
+ specified. (<literal>CASCADE</> will remove a dependent view entirely,
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if theforeign table does not exist. A notice is issued
+ in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of theforeign table to drop.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>CASCADE</literal></term>
+ <listitem>
+ <para>
+ Automatically drop objects that depend on theforeign table (such as
+ views).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Refuse to drop theforeign table if any objects depend on it. This is
+ the default.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+ To destroy two foreign tables, <literal>films</literal> and
+ <literal>distributors</literal>:
+
+<programlisting>
+DROP FOREIGN TABLE films, distributors;
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ This command conforms to the ISO/IEC 9075-9 (SQL/MED), except that the
+ standard only allows one foreign table to be dropped per command, and apart
+ from the <literal>IF EXISTS</> option, which is a <productname>PostgreSQL</>
+ extension.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-alterforeigntable"></member>
+ <member><xref linkend="sql-createforeigntable"></member>
+ </simplelist>
+ </refsect1>
+
+</refentry>
<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
<listitem>
<para>
- For each relation (table, view, index, or sequence) matching the
+ For each relation (table, view, index, sequence or foreign table)
+ matching the
<replaceable class="parameter">pattern</replaceable>, show all
columns, their types, the tablespace (if not the default) and any
special attributes such as <literal>NOT NULL</literal> or defaults.
- Associated indexes, constraints, rules, and triggers are
- also shown.
+ Associated indexes, constraints, rules, triggers are
+ also shown. If the relation was as foreign table, associated foreign
+ server is shown too.
(<quote>Matching the pattern</> is defined in
<xref linkend="APP-PSQL-patterns" endterm="APP-PSQL-patterns-title">
below.)
</varlistentry>
+ <varlistentry>
+ <term><literal>\det[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+ <listitem>
+ <para>
+ For each foreign table (mnemonic: <quote>external
+ tables</quote>) matching the
+ <replaceable class="parameter">pattern</replaceable>, show name
+ and associated foreign server. If the form <literal>\det+</literal>
+ is used, combined generic options is shown additionally.
+ </para>
+ </listitem>
+ </varlistentry>
+
+
<varlistentry>
<term><literal>\des[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<listitem>
&alterDefaultPrivileges;
&alterDomain;
&alterForeignDataWrapper;
+ &alterForeignTable;
&alterFunction;
&alterGroup;
&alterIndex;
&createDatabase;
&createDomain;
&createForeignDataWrapper;
+ &createForeignTable;
&createFunction;
&createGroup;
&createIndex;
&dropDatabase;
&dropDomain;
&dropForeignDataWrapper;
+ &dropForeignTable;
&dropFunction;
&dropGroup;
&dropIndex;
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_UNCATALOGED:
+ case RELKIND_FOREIGN_TABLE:
options = heap_reloptions(classForm->relkind, datum, false);
break;
case RELKIND_INDEX:
return (bytea *) rdopts;
case RELKIND_RELATION:
return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
+ case RELKIND_FOREIGN_TABLE:
+ if (validate && PointerIsValid(DatumGetPointer(reloptions)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("foreign table does not support storage options")));
+ return NULL;
default:
/* sequences, composite types and views are not supported */
return NULL;
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 \
)
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 */
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);
{
case ACL_OBJECT_RELATION:
case ACL_OBJECT_SEQUENCE:
+ case ACL_OBJECT_FOREIGN_TABLE:
ExecGrant_Relation(istmt);
break;
case ACL_OBJECT_DATABASE:
{
case ACL_OBJECT_RELATION:
case ACL_OBJECT_SEQUENCE:
+ case ACL_OBJECT_FOREIGN_TABLE:
foreach(cell, objnames)
{
RangeVar *relvar = (RangeVar *) lfirst(cell);
switch (objtype)
{
case ACL_OBJECT_RELATION:
- /* Process both regular tables and views */
+ /* Process both regular tables, views and foreign tables */
objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
objects = list_concat(objects, objs);
objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
objects = list_concat(objects, objs);
+ objs = getRelationsInNamespace(namespaceId, RELKIND_FOREIGN_TABLE);
+ objects = list_concat(objects, objs);
break;
case ACL_OBJECT_SEQUENCE:
objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
errmsg("\"%s\" is not a sequence",
NameStr(pg_class_tuple->relname))));
+ /* Used GRANT FOREIGN TABLE on a non-foreign-table? */
+ if (istmt->objtype == ACL_OBJECT_FOREIGN_TABLE &&
+ pg_class_tuple->relkind != RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a foreign table",
+ NameStr(pg_class_tuple->relname))));
+
/* Adjust the default permissions based on whether it is a sequence */
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
{
- if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
- this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
- else
- this_privileges = ACL_ALL_RIGHTS_RELATION;
+ switch (pg_class_tuple->relkind)
+ {
+ case RELKIND_SEQUENCE:
+ this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+ break;
+ case RELKIND_FOREIGN_TABLE:
+ this_privileges = ACL_ALL_RIGHTS_FOREIGN_TABLE;
+ break;
+ default:
+ this_privileges = ACL_ALL_RIGHTS_RELATION;
+ break;
+ }
}
else
this_privileges = istmt->privileges;
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))
&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;
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.
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);
this_privileges &= (AclMode) ACL_SELECT;
}
+ else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE &&
+ this_privileges & ~((AclMode) ACL_SELECT))
+ {
+ /* Foreign tables have the same restriction with sequences. */
+ ereport(WARNING,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("foreign table \"%s\" only supports SELECT column privileges",
+ NameStr(pg_class_tuple->relname))));
+ this_privileges &= (AclMode) ACL_SELECT;
+ }
expand_col_privileges(col_privs->cols, relOid,
this_privileges,
/* 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] =
/* 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"),
};
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
#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"
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
+ ForeignTableRelationId, /* OCLASS_FOREIGN_TABLE */
DefaultAclRelationId /* OCLASS_DEFACL */
};
Assert(object->objectSubId == 0);
return OCLASS_USER_MAPPING;
+ case ForeignTableRelationId:
+ Assert(object->objectSubId == 0);
+ return OCLASS_FOREIGN_TABLE;
+
case DefaultAclRelationId:
Assert(object->objectSubId == 0);
return OCLASS_DEFACL;
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"),
#include "catalog/namespace.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_constraint.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_statistic.h"
{
case RELKIND_VIEW:
case RELKIND_COMPOSITE_TYPE:
+ case RELKIND_FOREIGN_TABLE:
create_storage = false;
/*
new_rel_reltup->reltuples = 1;
break;
default:
- /* Views, etc, have no disk storage */
+ /* Views and foreign tables, etc, have no disk storage */
new_rel_reltup->relpages = 0;
new_rel_reltup->reltuples = 0;
break;
/* 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;
{
case RELKIND_RELATION:
case RELKIND_VIEW:
+ case RELKIND_FOREIGN_TABLE:
relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid,
relnamespace);
break;
* 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 ||
"it is being used by active queries in this session",
RelationGetRelationName(rel))));
+ /*
+ * Delete pg_foreign_table tuple first.
+ */
+ if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ Relation rel;
+ HeapTuple tuple;
+
+ rel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCache(FOREIGNTABLEREL,
+ ObjectIdGetDatum(relid), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for foreign table %u", relid);
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ ReleaseSysCache(tuple);
+ heap_close(rel, RowExclusiveLock);
+ }
+
/*
* Schedule unlinking of the relation's physical files at commit.
*/
if (rel->rd_rel->relkind != RELKIND_VIEW &&
- rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
+ rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
{
RelationDropStorage(rel);
}
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');
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
) 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,
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;
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,
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,
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')
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');
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;
'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
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,
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;
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;
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_TRIGGER:
+ case OBJECT_FOREIGN_TABLE:
{
Oid relid;
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
case OBJECT_INDEX:
+ case OBJECT_FOREIGN_TABLE:
{
/*
* RENAME TABLE requires that we (still) hold
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);
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));
RelationGetRelationName(relation));
/*
- * Allow comments only on columns of tables, views, and composite types
+ * Allow comments only on columns of tables, views, composite types, and foreign tables
* (which are the only relkinds for which pg_dump will dump per-column
* comments). In particular we wish to disallow comments on index
* columns, because the naming of an index's columns may change across PG
*/
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))));
}
#include "catalog/indexing.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "parser/parse_func.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+#include "utils/tqual.h"
/*
heap_close(rel, RowExclusiveLock);
}
+
+/*
+ * Create a foreign table
+ * call after DefineRelation().
+ */
+void
+CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
+{
+ Relation rel;
+ Datum ftoptions;
+ Datum values[Natts_pg_foreign_table];
+ bool nulls[Natts_pg_foreign_table];
+ HeapTuple tuple;
+ Oid ftId;
+ AclResult aclresult;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+ Oid ownerId;
+ ForeignDataWrapper *fdw;
+ ForeignServer *server;
+
+ /*
+ * For now the owner cannot be specified on create. Use effective user ID.
+ */
+ ownerId = GetUserId();
+
+ /*
+ * Check that the foreign server exists and that we have USAGE on it. Also
+ * get the actual FDW for option validation etc.
+ */
+ server = GetForeignServerByName(stmt->servername, false);
+ aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
+
+ fdw = GetForeignDataWrapper(server->fdwid);
+
+ /*
+ * Insert tuple into pg_foreign_table.
+ */
+ rel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
+ values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
+ /* Add table options */
+ ftoptions = transformGenericOptions(ForeignTableRelationId,
+ PointerGetDatum(NULL),
+ stmt->options,
+ fdw->fdwvalidator);
+
+ if (PointerIsValid(DatumGetPointer(ftoptions)))
+ values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
+ else
+ nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+ tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+ /* pg_foreign_table don't have OID */
+ ftId = simple_heap_insert(rel, tuple);
+
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ /* Add pg_class dependency on the server */
+ myself.classId = RelationRelationId;
+ myself.objectId = relid;
+ myself.objectSubId = 0;
+
+ referenced.classId = ForeignServerRelationId;
+ referenced.objectId = server->serverid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ heap_close(rel, NoLock);
+}
+
+/*
+ * ALTER FOREIGN TABLE <name> OPTIONS (...)
+ */
+void
+ATExecGenericOptions(Relation rel, List *options)
+{
+ ForeignTable *table;
+ ForeignServer *server;
+ ForeignDataWrapper *fdw;
+ HeapTuple fttp;
+ bool isnull;
+ Datum repl_val[Natts_pg_foreign_table];
+ bool repl_null[Natts_pg_foreign_table];
+ bool repl_repl[Natts_pg_foreign_table];
+ Datum datum;
+
+ if (options == NIL)
+ return;
+
+ fttp = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
+ if (!HeapTupleIsValid(fttp))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign table \"%s\" does not exist",
+ RelationGetRelationName(rel))));
+
+ table = GetForeignTable(rel->rd_id);
+ server = GetForeignServer(table->serverid);
+ fdw = GetForeignDataWrapper(server->fdwid);
+
+ memset(repl_val, 0, sizeof(repl_val));
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
+
+ /* Extract the current options */
+ datum = SysCacheGetAttr(FOREIGNTABLEREL,
+ fttp,
+ Anum_pg_foreign_table_ftoptions,
+ &isnull);
+ if (isnull)
+ datum = PointerGetDatum(NULL);
+
+ /* Transform the options */
+ datum = transformGenericOptions(ForeignTableRelationId,
+ datum,
+ options,
+ fdw->fdwvalidator);
+
+ if (PointerIsValid(DatumGetPointer(datum)))
+ repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
+ else
+ repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+ repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
+
+ /* Everything looks good - update the tuple */
+
+ rel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+ fttp = heap_modify_tuple(fttp, RelationGetDescr(rel),
+ repl_val, repl_null, repl_repl);
+
+ simple_heap_update(rel, &fttp->t_self, fttp);
+ CatalogUpdateIndexes(rel, fttp);
+
+ heap_close(rel, RowExclusiveLock);
+ heap_freetuple(fttp);
+}
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));
#include "catalog/namespace.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#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"
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}
};
static void ATRewriteTables(List **wqueue, LOCKMODE lockmode);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
-static void ATSimplePermissions(Relation rel, bool allowView, bool allowType);
+static void ATSimplePermissions(Relation rel, bool allowView, bool allowType,
+ bool allowForeignTable);
static void ATSimplePermissionsRelationOrIndex(Relation rel);
static void ATSimpleRecursion(List **wqueue, Relation rel,
AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
/*
* 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)
relkind = RELKIND_VIEW;
break;
+ case OBJECT_FOREIGN_TABLE:
+ relkind = RELKIND_FOREIGN_TABLE;
+ break;
+
default:
elog(ERROR, "unrecognized drop object type: %d",
(int) drop->removeType);
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))));
/*
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);
}
case AT_SetTableSpace: /* must rewrite heap */
case AT_DropNotNull: /* may change some SQL plans */
case AT_SetNotNull:
+ case AT_GenericOptions: /* may change any of foreign access */
cmd_lockmode = AccessExclusiveLock;
break;
switch (cmd->subtype)
{
case AT_AddColumn: /* ADD COLUMN */
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel, false, true, true);
/* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL;
break;
case AT_AddColumnToView: /* add column via CREATE OR REPLACE
* VIEW */
- ATSimplePermissions(rel, true, false);
+ ATSimplePermissions(rel, true, false, false);
/* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL;
* substitutes default values into INSERTs before it expands
* rules.
*/
- ATSimplePermissions(rel, true, false);
+ ATSimplePermissions(rel, true, false, true);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, true);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_DROP;
break;
case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, true);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_ADD_CONSTR;
pass = AT_PASS_MISC;
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, false);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_DropColumn: /* DROP COLUMN */
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel, false, true, true);
ATPrepDropColumn(rel, recurse, cmd);
/* Recursion occurs during execution phase */
pass = AT_PASS_DROP;
break;
case AT_AddIndex: /* ADD INDEX */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, false);
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_ADD_INDEX;
break;
case AT_AddConstraint: /* ADD CONSTRAINT */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, true);
/* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */
if (recurse)
pass = AT_PASS_ADD_CONSTR;
break;
case AT_DropConstraint: /* DROP CONSTRAINT */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, true);
/* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */
if (recurse)
pass = AT_PASS_DROP;
break;
case AT_AlterColumnType: /* ALTER COLUMN TYPE */
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel, false, true, true);
/* Performs own recursion */
ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ALTER_TYPE;
break;
case AT_ClusterOn: /* CLUSTER ON */
case AT_DropCluster: /* SET WITHOUT CLUSTER */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, false);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_AddOids: /* SET WITH OIDS */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, true);
/* Performs own recursion */
if (!rel->rd_rel->relhasoids || recursing)
ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL;
break;
case AT_DropOids: /* SET WITHOUT OIDS */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, true);
/* Performs own recursion */
if (rel->rd_rel->relhasoids)
{
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, true);
/* This command never recurses */
ATPrepAddInherit(rel);
pass = AT_PASS_MISC;
case AT_DisableTrig: /* DISABLE TRIGGER variants */
case AT_DisableTrigAll:
case AT_DisableTrigUser:
+ ATSimplePermissions(rel, false, false, false);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
case AT_DropInherit: /* NO INHERIT */
- ATSimplePermissions(rel, false, false);
+ case AT_GenericOptions:
+ ATSimplePermissions(rel, false, false, true);
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
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);
{
AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
+ /* If it's a foreign table, no need to rewrite it. */
+ if (tab->relkind == RELKIND_FOREIGN_TABLE)
+ continue;
+
/*
* We only need to rewrite the table if at least one column needs to
* be recomputed, or we are adding/removing the OID column.
BulkInsertState bistate;
int hi_options;
+
/*
* Open the relation(s). We have surely already locked the existing
* table.
/*
* ATSimplePermissions
*
- * - Ensure that it is a relation (or possibly a view)
+ * - Ensure that it is a relation (or possibly a view or composite type or
+ * foreign table)
* - Ensure this user is the owner
* - Ensure that it is not a system table
*/
static void
-ATSimplePermissions(Relation rel, bool allowView, bool allowType)
+ATSimplePermissions(Relation rel, bool allowView, bool allowType,
+ bool allowForeignTable)
{
if (rel->rd_rel->relkind != RELKIND_RELATION)
{
- if (allowView)
- {
- if (rel->rd_rel->relkind != RELKIND_VIEW)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or view",
- RelationGetRelationName(rel))));
- }
- else if (allowType)
- {
- if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ int relkind = rel->rd_rel->relkind;
+ StringInfoData allowed;
+
+ /* what kind of object is allowed ? */
+ initStringInfo(&allowed);
+ appendStringInfo(&allowed, "a table%s%s%s",
+ allowView ? " or view" : "",
+ allowType ? " or composite type" : "",
+ allowForeignTable ? " or foreign table" : "");
+
+ if ((relkind == RELKIND_VIEW && !allowView) ||
+ (relkind == RELKIND_COMPOSITE_TYPE && !allowType) ||
+ (relkind == RELKIND_FOREIGN_TABLE && !allowForeignTable))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or composite type",
- RelationGetRelationName(rel))));
- }
- else
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table",
- RelationGetRelationName(rel))));
+ errmsg("\"%s\" is not %s",
+ RelationGetRelationName(rel), allowed.data)));
+
+ pfree(allowed.data);
}
/* Permissions checks */
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel, false, true, true);
/*
* get the number of the attribute
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, true);
/*
* Call AddRelationNewConstraints to do the work, making sure it works on
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, false, false, true);
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
{
case RELKIND_RELATION:
case RELKIND_VIEW:
+ case RELKIND_FOREIGN_TABLE:
/* ok to change owner */
break;
case RELKIND_INDEX:
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))));
}
* Must be owner of both parent and child -- child was checked by
* ATSimplePermissions call in ATPrepCmd
*/
- ATSimplePermissions(parent_rel, false, false);
+ ATSimplePermissions(parent_rel, false, false, true);
/* Permanent rels cannot inherit from temporary ones */
if (parent_rel->rd_istemp && !child_rel->rd_istemp)
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);
}
{
case RELKIND_RELATION:
case RELKIND_VIEW:
+ case RELKIND_FOREIGN_TABLE:
/* ok to change schema */
break;
case RELKIND_SEQUENCE:
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))));
}
errmsg("cannot change view \"%s\"",
RelationGetRelationName(resultRelationDesc))));
break;
+ case RELKIND_FOREIGN_TABLE:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot change foreign table \"%s\"",
+ RelationGetRelationName(resultRelationDesc))));
+ break;
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
#include "catalog/namespace.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "foreign/foreign.h"
server = palloc(sizeof(ForeignServer));
server->serverid = serverid;
+ server->fdwid = serverform->srvfdw;
+ server->owner = serverform->srvowner;
server->servername = pstrdup(NameStr(serverform->srvname));
server->owner = serverform->srvowner;
server->fdwid = serverform->srvfdw;
PG_RETURN_BOOL(true);
}
+
+/*
+ * GetForeignTable - look up the foreign table definition by relation oid.
+ */
+ForeignTable *
+GetForeignTable(Oid relid)
+{
+ Form_pg_foreign_table tableform;
+ ForeignTable *ft;
+ HeapTuple tp;
+ Datum datum;
+ bool isnull;
+
+ tp = SearchSysCache(FOREIGNTABLEREL,
+ ObjectIdGetDatum(relid),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for foreign table %u", relid);
+
+ tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
+
+ ft = palloc(sizeof(ForeignTable));
+ ft->relid = relid;
+ ft->serverid = tableform->ftserver;
+
+ /* Extract the ftoptions */
+ datum = SysCacheGetAttr(FOREIGNTABLEREL,
+ tp,
+ Anum_pg_foreign_table_ftoptions,
+ &isnull);
+
+ /* untransformRelOptions does exactly what we want - avoid duplication */
+ ft->options = untransformRelOptions(datum);
+ ReleaseSysCache(tp);
+
+ return ft;
+}
+
+/*
+ * Determine the relation is a foreign table.
+ */
+bool
+is_foreign_table(Oid relid)
+{
+ HeapTuple tup;
+ char relkind;
+ bool isnull;
+
+ tup = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0);
+ relkind = CharGetDatum(
+ SysCacheGetAttr(RELOID, tup, Anum_pg_class_relkind, &isnull));
+ Assert(!isnull);
+ ReleaseSysCache(tup);
+
+ return (relkind == RELKIND_FOREIGN_TABLE);
+}
return newnode;
}
+static CreateForeignTableStmt *
+_copyCreateForeignTableStmt(CreateForeignTableStmt *from)
+{
+ CreateForeignTableStmt *newnode = makeNode(CreateForeignTableStmt);
+
+ COPY_NODE_FIELD(base.relation);
+ COPY_NODE_FIELD(base.tableElts);
+ COPY_NODE_FIELD(base.inhRelations);
+ COPY_NODE_FIELD(base.ofTypename);
+ COPY_NODE_FIELD(base.constraints);
+ COPY_NODE_FIELD(base.options);
+ COPY_SCALAR_FIELD(base.oncommit);
+ COPY_STRING_FIELD(base.tablespacename);
+ COPY_STRING_FIELD(servername);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
static CreateTrigStmt *
_copyCreateTrigStmt(CreateTrigStmt *from)
{
case T_DropUserMappingStmt:
retval = _copyDropUserMappingStmt(from);
break;
+ case T_CreateForeignTableStmt:
+ retval = _copyCreateForeignTableStmt(from);
+ break;
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
return true;
}
+static bool
+_equalCreateForeignTableStmt(CreateForeignTableStmt *a, CreateForeignTableStmt *b)
+{
+ if (!_equalCreateStmt(&a->base, &b->base))
+ return false;
+
+ COMPARE_STRING_FIELD(base.tablespacename);
+ COMPARE_STRING_FIELD(servername);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
static bool
_equalCreateTrigStmt(CreateTrigStmt *a, CreateTrigStmt *b)
{
case T_DropUserMappingStmt:
retval = _equalDropUserMappingStmt(a, b);
break;
+ case T_CreateForeignTableStmt:
+ retval = _equalCreateForeignTableStmt(a, b);
+ break;
case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
#include <math.h>
+#include "foreign/foreign.h"
#include "nodes/nodeFuncs.h"
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
* least one dimension of cost or sortedness.
*/
+ /*
+ * TODO: Implement foreign table scanning and replace this check with
+ * path creation for foreign table.
+ */
+ if (is_foreign_table(rte->relid))
+ elog(ERROR, "foreign table scan not implemented.");
+
/* Consider sequential scan */
add_path(rel, create_seqscan_path(root, rel));
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
+ AlterForeignTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
AlterDefaultPrivilegesStmt DefACLAction
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
%type <node> alter_column_default opclass_item opclass_drop alter_using
%type <ival> add_drop opt_asc_desc opt_nulls_order
-%type <node> alter_table_cmd alter_type_cmd
-%type <list> alter_table_cmds alter_type_cmds
+%type <node> alter_table_cmd alter_foreign_table_cmd alter_type_cmd
+%type <list> alter_table_cmds alter_foreign_table_cmds alter_type_cmds
%type <dbehavior> opt_drop_behavior
%type <list> stmtblock stmtmulti
OptTableElementList TableElementList OptInherit definition
OptTypedTableElementList TypedTableElementList
+ OptForeignTableElementList ForeignTableElementList
+ OptTypedForeignTableElementList TypedForeignTableElementList
reloptions opt_reloptions
OptWith opt_distinct opt_definition func_args func_args_list
func_args_with_defaults func_args_with_defaults_list
%type <vsetstmt> set_rest SetResetClause
%type <node> TableElement TypedTableElement ConstraintElem TableFuncElement
+ ForeignTableElement TypedForeignTableElement
+ ForeignConstraintElem
+%type <node> CheckConstraintElem UniqueConstraintElem PrimaryKeyConstraintElem
+ ExcludeConstraintElem ForeignKeyConstraintElem
%type <node> columnDef columnOptions
+ foreignColumnDef
%type <defelt> def_elem reloption_elem old_aggr_elem
%type <node> def_arg columnElem where_clause where_or_current_clause
a_expr b_expr c_expr func_expr AexprConst indirection_el
%type <keyword> unreserved_keyword type_func_name_keyword
%type <keyword> col_name_keyword reserved_keyword
-%type <node> TableConstraint TableLikeClause
+%type <node> TableConstraint TableLikeClause ForeignTableLikeClause
+ ForeignTableConstraint
%type <ival> TableLikeOptionList TableLikeOption
-%type <list> ColQualList
+%type <ival> ForeignTableLikeOptionList ForeignTableLikeOption
+%type <list> ColQualList ForeignColQualList
%type <node> ColConstraint ColConstraintElem ConstraintAttr
+ ColNotNullConstraintElem ColNullConstraintElem
+ ColCheckConstraintElem
+ ForeignColConstraint ForeignColConstraintElem
%type <ival> key_actions key_delete key_match key_update key_action
%type <ival> ConstraintAttributeSpec ConstraintDeferrabilitySpec
ConstraintTimeSpec
| AlterDomainStmt
| AlterFdwStmt
| AlterForeignServerStmt
+ | AlterForeignTableStmt
| AlterFunctionStmt
| AlterGroupStmt
| AlterObjectSchemaStmt
| CreateDomainStmt
| CreateFdwStmt
| CreateForeignServerStmt
+ | CreateForeignTableStmt
| CreateFunctionStmt
| CreateGroupStmt
| CreateOpClassStmt
n->subtype = AT_DropOids;
$$ = (Node *)n;
}
+
/* ALTER TABLE <name> CLUSTER ON <indexname> */
| CLUSTER ON name
{
* or be part of a_expr NOT LIKE or similar constructs).
*/
ColConstraintElem:
- NOT NULL_P
- {
- Constraint *n = makeNode(Constraint);
- n->contype = CONSTR_NOTNULL;
- n->location = @1;
- $$ = (Node *)n;
- }
- | NULL_P
- {
- Constraint *n = makeNode(Constraint);
- n->contype = CONSTR_NULL;
- n->location = @1;
- $$ = (Node *)n;
- }
+ ColNotNullConstraintElem { $$ = $1; }
+ | ColNullConstraintElem { $$ = $1; }
| UNIQUE opt_definition OptConsTableSpace
{
Constraint *n = makeNode(Constraint);
n->indexspace = $4;
$$ = (Node *)n;
}
- | CHECK '(' a_expr ')'
- {
- Constraint *n = makeNode(Constraint);
- n->contype = CONSTR_CHECK;
- n->location = @1;
- n->raw_expr = $3;
- n->cooked_expr = NULL;
- $$ = (Node *)n;
- }
+ | ColCheckConstraintElem { $$ = $1; }
| DEFAULT b_expr
{
Constraint *n = makeNode(Constraint);
}
;
+ColNotNullConstraintElem: NOT NULL_P
+ {
+ Constraint *n = makeNode(Constraint);
+ n->contype = CONSTR_NOTNULL;
+ n->location = @1;
+ $$ = (Node *)n;
+ }
+ ;
+
+ColNullConstraintElem: NULL_P
+ {
+ Constraint *n = makeNode(Constraint);
+ n->contype = CONSTR_NULL;
+ n->location = @1;
+ $$ = (Node *)n;
+ }
+ ;
+
+ColCheckConstraintElem: CHECK '(' a_expr ')'
+ {
+ Constraint *n = makeNode(Constraint);
+ n->contype = CONSTR_CHECK;
+ n->location = @1;
+ n->raw_expr = $3;
+ n->cooked_expr = NULL;
+ $$ = (Node *)n;
+ }
+ ;
+
/*
* ConstraintAttr represents constraint attributes, which we parse as if
* they were independent constraint clauses, in order to avoid shift/reduce
| ALL { $$ = CREATE_TABLE_LIKE_ALL; }
;
+ForeignTableLikeClause:
+ LIKE qualified_name ForeignTableLikeOptionList
+ {
+ InhRelation *n = makeNode(InhRelation);
+ n->relation = $2;
+ n->options = $3;
+ $$ = (Node *)n;
+ }
+ ;
+
+ForeignTableLikeOptionList:
+ ForeignTableLikeOptionList INCLUDING ForeignTableLikeOption
+ { $$ = $1 | $3; }
+ | ForeignTableLikeOptionList EXCLUDING ForeignTableLikeOption
+ { $$ = $1 & ~$3; }
+ | /* EMPTY */ { $$ = 0; }
+ ;
+
+ForeignTableLikeOption:
+ DEFAULTS { $$ = CREATE_TABLE_LIKE_DEFAULTS; }
+ | CONSTRAINTS { $$ = CREATE_TABLE_LIKE_CONSTRAINTS; }
+ | COMMENTS { $$ = CREATE_TABLE_LIKE_COMMENTS; }
+ | ALL { $$ = CREATE_TABLE_LIKE_FOREIGN_ALL; }
+ ;
+
/* ConstraintElem specifies constraint syntax which is not embedded into
* a column definition. ColConstraintElem specifies the embedded form.
| ConstraintElem { $$ = $1; }
;
+ForeignTableConstraint:
+ CONSTRAINT name ForeignConstraintElem
+ {
+ Constraint *n = (Constraint *) $3;
+ Assert(IsA(n, Constraint));
+ n->conname = $2;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | ForeignConstraintElem { $$ = $1; }
+ ;
+
+
ConstraintElem:
+ CheckConstraintElem { $$ = $1; }
+ | UniqueConstraintElem { $$ = $1; }
+ | PrimaryKeyConstraintElem { $$ = $1; }
+ | ExcludeConstraintElem { $$ = $1; }
+ | ForeignKeyConstraintElem { $$ = $1; }
+ ;
+
+ForeignConstraintElem:
+ CheckConstraintElem { $$ = $1; }
+ ;
+
+CheckConstraintElem:
CHECK '(' a_expr ')' ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
parser_errposition(@5)));
$$ = (Node *)n;
}
- | UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
+ ;
+
+UniqueConstraintElem:
+ UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->initdeferred = ($7 & 2) != 0;
$$ = (Node *)n;
}
- | PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
+ ;
+
+PrimaryKeyConstraintElem:
+ PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->initdeferred = ($8 & 2) != 0;
$$ = (Node *)n;
}
- | EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
+ ;
+
+ExcludeConstraintElem:
+ EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
opt_definition OptConsTableSpace ExclusionWhereClause
ConstraintAttributeSpec
{
n->initdeferred = ($9 & 2) != 0;
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
+ ;
+ForeignKeyConstraintElem:
+ FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
opt_column_list key_match key_actions ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
}
;
+/*****************************************************************************
+ *
+ * QUERY:
+ * CREATE FORIGN TABLE relname (...) SERVER name (...)
+ *
+ *****************************************************************************/
+
+CreateForeignTableStmt:
+ CREATE FOREIGN TABLE qualified_name
+ OptForeignTableElementList OptInherit
+ SERVER name OptWith create_generic_options
+ {
+ CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt);
+ $4->istemp = false;
+ n->base.relation = $4;
+ n->base.tableElts = $5;
+ n->base.inhRelations = $6;
+ n->base.options = $9;
+ /* FDW-specific data */
+ n->servername = $8;
+ n->options = $10;
+ $$ = (Node *) n;
+ }
+ | CREATE FOREIGN TABLE qualified_name OF any_name
+ OptTypedForeignTableElementList
+ SERVER name OptWith create_generic_options
+ {
+ CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt);
+ $4->istemp = false;
+ n->base.relation = $4;
+ n->base.tableElts = $7;
+ n->base.ofTypename = makeTypeNameFromNameList($6);
+ n->base.ofTypename->location = @6;
+ n->base.options = $10;
+ /* FDW-specific data */
+ n->servername = $9;
+ n->options = $11;
+ $$ = (Node *) n;
+ }
+ ;
+
+OptForeignTableElementList:
+ '(' ForeignTableElementList ')' { $$ = $2; }
+ | '(' ')' { $$ = NIL; }
+ | /*EMPTY*/
+ {
+ /* TODO: import definitions from foreign server in this case. */
+ $$ = NIL;
+ }
+ ;
+
+OptTypedForeignTableElementList:
+ '(' TypedForeignTableElementList ')' { $$ = $2; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+ForeignTableElementList:
+ ForeignTableElement
+ {
+ $$ = list_make1($1);
+ }
+ | ForeignTableElementList ',' ForeignTableElement
+ {
+ $$ = lappend($1, $3);
+ }
+ ;
+
+TypedForeignTableElementList:
+ TypedForeignTableElement
+ {
+ $$ = list_make1($1);
+ }
+ | TypedForeignTableElementList ',' TypedForeignTableElement
+ {
+ $$ = lappend($1, $3);
+ }
+ ;
+
+ForeignTableElement:
+ foreignColumnDef { $$ = $1; }
+ | ForeignTableLikeClause { $$ = $1; }
+ | ForeignTableConstraint { $$ = $1; }
+ ;
+
+TypedForeignTableElement:
+ columnOptions { $$ = $1; }
+ | ForeignTableConstraint { $$ = $1; }
+ ;
+
+/* TODO: support GENERIC OPTIONS of foreign column */
+foreignColumnDef: ColId Typename ForeignColQualList
+ {
+ ColumnDef *n = makeNode(ColumnDef);
+ n->colname = $1;
+ n->typeName = $2;
+ n->constraints = $3;
+ n->is_local = true;
+ $$ = (Node *)n;
+ }
+ ;
+
+ForeignColQualList:
+ ForeignColQualList ForeignColConstraint { $$ = lappend($1, $2); }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+ForeignColConstraint:
+ CONSTRAINT name ForeignColConstraintElem
+ {
+ Constraint *n = (Constraint *) $3;
+ Assert(IsA(n, Constraint));
+ n->conname = $2;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | ForeignColConstraintElem { $$ = $1; }
+ | DEFAULT b_expr
+ {
+ Constraint *n = makeNode(Constraint);
+ n->contype = CONSTR_DEFAULT;
+ n->location = @1;
+ n->raw_expr = $2;
+ n->cooked_expr = NULL;
+ $$ = (Node *)n;
+ }
+ ;
+
+ForeignColConstraintElem:
+ ColNotNullConstraintElem { $$ = $1; }
+ | ColNullConstraintElem { $$ = $1; }
+ | ColCheckConstraintElem { $$ = $1; }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
+ * ALTER FORIGN TABLE relname ADD [COLUMN] column-definition;
+ * ALTER FORIGN TABLE relname ALTERR [COLUMN] name OPTIONS (...);
+ * ALTER FORIGN TABLE relname DROP [COLUMN] name [CASCADE];
+ * ALTER FORIGN TABLE relname OPTIONS (...);
+ *
+ *****************************************************************************/
+AlterForeignTableStmt:
+ ALTER FOREIGN TABLE relation_expr alter_foreign_table_cmds
+ {
+ AlterTableStmt *n = makeNode(AlterTableStmt);
+ n->relation = $4;
+ n->cmds = $5;
+ n->relkind = OBJECT_FOREIGN_TABLE;
+ $$ = (Node *)n;
+ }
+ ;
+
+alter_foreign_table_cmds:
+ alter_foreign_table_cmd { $$ = list_make1($1); }
+ | alter_foreign_table_cmds ',' alter_foreign_table_cmd
+ { $$ = lappend($1, $3); }
+ ;
+
+alter_foreign_table_cmd:
+ /* ALTER FOREIGN TABLE <name> ADD <coldef> */
+ ADD_P columnDef
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AddColumn;
+ n->def = $2;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> ADD COLUMN <coldef> */
+ | ADD_P COLUMN columnDef
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AddColumn;
+ n->def = $3;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> {SET DEFAULT <expr>|DROP DEFAULT} */
+ | ALTER opt_column ColId alter_column_default
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_ColumnDefault;
+ n->name = $3;
+ n->def = $4;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> DROP NOT NULL */
+ | ALTER opt_column ColId DROP NOT NULL_P
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropNotNull;
+ n->name = $3;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> SET NOT NULL */
+ | ALTER opt_column ColId SET NOT NULL_P
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetNotNull;
+ n->name = $3;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
+ | DROP opt_column IF_P EXISTS ColId opt_drop_behavior
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropColumn;
+ n->name = $5;
+ n->behavior = $6;
+ n->missing_ok = TRUE;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> DROP [COLUMN] <colname> [RESTRICT|CASCADE] */
+ | DROP opt_column ColId opt_drop_behavior
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropColumn;
+ n->name = $3;
+ n->behavior = $4;
+ n->missing_ok = FALSE;
+ $$ = (Node *)n;
+ }
+ /*
+ * ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> [SET DATA] TYPE <typename>
+ */
+ | ALTER opt_column ColId opt_set_data TYPE_P Typename
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AlterColumnType;
+ n->name = $3;
+ n->def = (Node *) $6;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> ADD CONSTRAINT ... */
+ | ADD_P ForeignTableConstraint
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AddConstraint;
+ n->def = $2;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> DROP CONSTRAINT IF EXISTS <name> [RESTRICT|CASCADE] */
+ | DROP CONSTRAINT IF_P EXISTS name opt_drop_behavior
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropConstraint;
+ n->name = $5;
+ n->behavior = $6;
+ n->missing_ok = TRUE;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */
+ | DROP CONSTRAINT name opt_drop_behavior
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropConstraint;
+ n->name = $3;
+ n->behavior = $4;
+ n->missing_ok = FALSE;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> SET WITH OIDS */
+ | SET WITH OIDS
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AddOids;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> SET WITHOUT OIDS */
+ | SET WITHOUT OIDS
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropOids;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> ENABLE RULE <rule> */
+ | ENABLE_P RULE name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_EnableRule;
+ n->name = $3;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> ENABLE ALWAYS RULE <rule> */
+ | ENABLE_P ALWAYS RULE name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_EnableAlwaysRule;
+ n->name = $4;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> ENABLE REPLICA RULE <rule> */
+ | ENABLE_P REPLICA RULE name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_EnableReplicaRule;
+ n->name = $4;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> DISABLE RULE <rule> */
+ | DISABLE_P RULE name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DisableRule;
+ n->name = $3;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> INHERIT <parent> */
+ | INHERIT qualified_name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AddInherit;
+ n->def = (Node *) $2;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> NO INHERIT <parent> */
+ | NO INHERIT qualified_name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropInherit;
+ n->def = (Node *) $3;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> OWNER TO RoleId */
+ | OWNER TO RoleId
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_ChangeOwner;
+ n->name = $3;
+ $$ = (Node *)n;
+ }
+ /* ALTER FOREIGN TABLE <name> OPTIONS (...) */
+ | alter_generic_options
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_GenericOptions;
+ n->def = (Node *)$1;
+ $$ = (Node *) n;
+ }
+ ;
+
/*****************************************************************************
*
* QUERY:
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
| TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; }
+ | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
;
any_name_list:
* 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) |
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
| ROLE { $$ = OBJECT_ROLE; }
+ | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
;
comment_text:
| DOMAIN_P { $$ = OBJECT_TYPE; }
| TYPE_P { $$ = OBJECT_TYPE; }
| VIEW { $$ = OBJECT_VIEW; }
+ | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
;
security_label: Sconst { $$ = $1; }
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));
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->newname = $8;
$$ = (Node *)n;
}
+ | ALTER FOREIGN TABLE relation_expr RENAME opt_column name TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_COLUMN;
+ n->relation = $4;
+ n->subname = $7;
+ n->newname = $9;
+ $$ = (Node *)n;
+ }
| ALTER TRIGGER name ON qualified_name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
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);
/* 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 */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
- cxt.stmtType = "CREATE TABLE";
+ if (IsA(stmt, CreateForeignServerStmt))
+ cxt.stmtType = "CREATE FOREIGN TABLE";
+ else
+ cxt.stmtType = "CREATE TABLE";
cxt.relation = stmt->relation;
cxt.rel = NULL;
cxt.inhRelations = stmt->inhRelations;
* Verify relation is of a type that rules can sensibly be applied to.
*/
if (event_relation->rd_rel->relkind != RELKIND_RELATION &&
- event_relation->rd_rel->relkind != RELKIND_VIEW)
+ event_relation->rd_rel->relkind != RELKIND_VIEW &&
+ event_relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or view",
+ errmsg("\"%s\" is not a table, view or foreign table",
RelationGetRelationName(event_relation))));
if (!allowSystemTableMods && IsSystemRelation(event_relation))
if (event_type == CMD_SELECT)
{
+ /* Foreign tables can't be changed to view. */
+ if (event_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot define rule on SELECT to a foreign table")));
+ }
+
/*
* Rules ON SELECT are restricted to view definitions
*
case T_AlterUserMappingStmt:
case T_DropUserMappingStmt:
case T_AlterTableSpaceOptionsStmt:
+ case T_CreateForeignTableStmt:
case T_SecLabelStmt:
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
break;
break;
case T_CreateStmt:
+ case T_CreateForeignTableStmt:
{
List *stmts;
ListCell *l;
AlterTableCreateToastTable(relOid, toast_options);
}
+ else if (IsA(stmt, CreateForeignTableStmt))
+ {
+ /* Create the table itself */
+ relOid = DefineRelation((CreateStmt *) stmt,
+ RELKIND_FOREIGN_TABLE,
+ InvalidOid);
+
+ CreateForeignTable((CreateForeignTableStmt *) stmt,
+ relOid);
+ }
else
{
/* Recurse for anything else */
case OBJECT_SEQUENCE:
case OBJECT_VIEW:
case OBJECT_INDEX:
+ case OBJECT_FOREIGN_TABLE:
RemoveRelations(stmt);
break;
tag = "DROP USER MAPPING";
break;
+ case T_CreateForeignTableStmt:
+ tag = "CREATE FOREIGN TABLE";
+ break;
+
case T_DropStmt:
switch (((DropStmt *) parsetree)->removeType)
{
case OBJECT_TSCONFIGURATION:
tag = "DROP TEXT SEARCH CONFIGURATION";
break;
+ case OBJECT_FOREIGN_TABLE:
+ tag = "DROP FOREIGN TABLE";
+ break;
default:
tag = "???";
}
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;
case OBJECT_VIEW:
tag = "ALTER VIEW";
break;
+ case OBJECT_FOREIGN_TABLE:
+ tag = "ALTER FOREIGN TABLE";
+ break;
default:
tag = "???";
break;
case OBJECT_FOREIGN_SERVER:
tag = "ALTER SERVER";
break;
+ case OBJECT_FOREIGN_TABLE:
+ tag = "ALTER FOREIGN TABLE";
+ break;
default:
tag = "???";
break;
case OBJECT_VIEW:
tag = "ALTER VIEW";
break;
+ case OBJECT_FOREIGN_TABLE:
+ tag = "ALTER FOREIGN TABLE";
+ break;
default:
tag = "???";
break;
break;
case T_CreateStmt:
+ case T_CreateForeignTableStmt:
lev = LOGSTMT_DDL;
break;
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 */
#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"
},
32
},
+ {ForeignTableRelationId, /* FOREIGNTABLEREL */
+ ForeignTableRelidIndexId,
+ 1,
+ {
+ Anum_pg_foreign_table_ftrelid,
+ 0,
+ 0,
+ 0
+ },
+ 128
+ },
{IndexRelationId, /* INDEXRELID */
IndexRelidIndexId,
1,
for (i = 0; i < numTables; i++)
{
- /* Sequences and views never have parents */
+ /* Sequences and views and foreign tables never have parents */
if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
tblinfo[i].relkind == RELKIND_VIEW)
continue;
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");
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)
{
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 ||
"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)");
/* Skip SEQUENCEs (handled elsewhere) */
if (tblinfo[i].relkind == RELKIND_SEQUENCE)
continue;
+ /* Skip FOREIGN TABLEs (no data to dump) */
+ if (tblinfo[i].relkind == RELKIND_FOREIGN_TABLE)
+ continue;
if (tblinfo[i].dobj.dump)
{
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
- if (g_fout->remoteVersion >= 90000)
+ if (g_fout->remoteVersion >= 90100)
+ {
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * owning column, if any (note this dependency is AUTO as of 8.2)
+ */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, c.relname, "
+ "c.relacl, c.relkind, c.relnamespace, "
+ "(%s c.relowner) AS rolname, "
+ "c.relchecks, c.relhastriggers, "
+ "c.relhasindex, c.relhasrules, c.relhasoids, "
+ "c.relfrozenxid, "
+ "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
+ "d.refobjid AS owning_tab, "
+ "d.refobjsubid AS owning_col, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
+ "array_to_string(c.reloptions, ', ') AS reloptions, "
+ "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+ "FROM pg_class c "
+ "LEFT JOIN pg_depend d ON "
+ "(c.relkind = '%c' AND "
+ "d.classid = c.tableoid AND d.objid = c.oid AND "
+ "d.objsubid = 0 AND "
+ "d.refclassid = c.tableoid AND d.deptype = 'a') "
+ "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
+ "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') "
+ "ORDER BY c.oid",
+ username_subquery,
+ RELKIND_SEQUENCE,
+ RELKIND_RELATION, RELKIND_SEQUENCE,
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
+ RELKIND_FOREIGN_TABLE);
+ }
+ else if (g_fout->remoteVersion >= 90000)
{
/*
* Left join to pick up dependency info linking sequences to their
* NOTE: it'd be kinda nice to lock views and sequences too, not only
* plain tables, but the backend doesn't presently allow that.
*/
- if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION)
+ if (tblinfo[i].dobj.dump && (tblinfo[i].relkind == RELKIND_RELATION ||
+ tblinfo[i].relkind == RELKIND_FOREIGN_TABLE))
{
resetPQExpBuffer(query);
appendPQExpBuffer(query,
/* 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);
int j,
k;
bool toast_set = false;
+ char *srvname;
+ char *ftoptions;
/* Make sure we are in proper schema */
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
}
else
{
- reltypename = "TABLE";
+ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ int i_srvname;
+ int i_ftoptions;
+
+ reltypename = "FOREIGN TABLE";
+
+ /* retrieve name of foreign server and generic options */
+ appendPQExpBuffer(query,
+ "SELECT fs.srvname, array_to_string(ARRAY("
+ " SELECT option_name || ' ' || quote_literal(option_value)"
+ " FROM pg_options_to_table(ftoptions)), ', ') AS ftoptions "
+ "FROM pg_foreign_table ft JOIN pg_foreign_server fs "
+ " ON (fs.oid = ft.ftserver) "
+ "WHERE ft.ftrelid = %u", tbinfo->dobj.catId.oid);
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+ i_srvname = PQfnumber(res, "srvname");
+ i_ftoptions = PQfnumber(res, "ftoptions");
+ srvname = strdup(PQgetvalue(res, 0, i_srvname));
+ ftoptions = strdup(PQgetvalue(res, 0, i_ftoptions));
+ PQclear(res);
+ }
+ else
+ reltypename = "TABLE";
numParents = tbinfo->numParents;
parents = tbinfo->parents;
* 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));
if (binary_upgrade)
binary_upgrade_set_relfilenodes(q, tbinfo->dobj.catId.oid, false);
- appendPQExpBuffer(q, "CREATE TABLE %s",
+ appendPQExpBuffer(q, "CREATE %s %s", reltypename,
fmtId(tbinfo->dobj.name));
if (tbinfo->reloftype)
appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
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))
{
appendPQExpBuffer(q, ")");
}
+ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
+ appendPQExpBuffer(q, "\nOPTIONS (%s)", ftoptions);
+
appendPQExpBuffer(q, ";\n");
/*
* 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++)
{
ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
tbinfo->dobj.name,
tbinfo->dobj.namespace->dobj.name,
- (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
+ (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
tbinfo->rolname,
(strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
reltypename, SECTION_PRE_DATA,
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);
case 'w':
success = listForeignDataWrappers(pattern, show_verbose);
break;
+ case 't':
+ success = listForeignTables(pattern, show_verbose);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
break;
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");
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
" 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"
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\"",
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");
PQclear(result);
}
}
- else if (tableinfo.relkind == 'r')
+ else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f')
{
/* Footer information about a table */
PGresult *result = NULL;
PQclear(result);
}
+ /* print foreign server name */
+ if (tableinfo.relkind == 'f')
+ {
+ /* Footer information about foreign table */
+ printfPQExpBuffer(&buf,
+ "SELECT s.srvname\n"
+ "FROM pg_catalog.pg_foreign_table f,\n"
+ " pg_catalog.pg_foreign_server s\n"
+ "WHERE f.ftrelid = %s AND s.oid = f.ftserver",
+ oid);
+ result = PSQLexec(buf.data, false);
+ if (!result)
+ goto error_return;
+ else if (PQntuples(result) != 1)
+ {
+ PQclear(result);
+ goto error_return;
+ }
+
+ printfPQExpBuffer(&buf, "Server: %s",
+ PQgetvalue(result, 0, 0));
+ printTableAddFooter(&cont, buf.data);
+ PQclear(result);
+ }
+
/* print inherited tables */
printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid);
* i - indexes
* v - views
* s - sequences
+ * e - foreign table(Note: different from 'f' for relkind)
* (any order of the above is fine)
* If tabtypes is empty, we default to \dtvs.
*/
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;
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"),
gettext_noop("index"),
gettext_noop("sequence"),
gettext_noop("special"),
+ gettext_noop("foreign table"),
gettext_noop("Type"),
gettext_noop("Owner"));
if (showSystem || pattern)
appendPQExpBuffer(&buf, "'s',"); /* was RELKIND_SPECIAL in <=
* 8.1 */
+ if (showForeign)
+ appendPQExpBuffer(&buf, "'f',");
+
appendPQExpBuffer(&buf, "''"); /* dummy */
appendPQExpBuffer(&buf, ")\n");
return true;
}
+/*
+ * \det
+ *
+ * Describes foreign tables.
+ */
+bool
+listForeignTables(const char *pattern, bool verbose)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ if (pset.sversion < 90100)
+ {
+ fprintf(stderr, _("The server (version %d.%d) does not support foreign table.\n"),
+ pset.sversion / 10000, (pset.sversion / 100) % 100);
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT c.relname AS \"%s\",\n"
+ " s.srvname AS \"%s\"",
+ gettext_noop("Table"),
+ gettext_noop("Server"));
+
+ if (verbose)
+ appendPQExpBuffer(&buf,
+ ",\n ft.ftoptions AS \"%s\"",
+ gettext_noop("Options"));
+
+ appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
+ appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
+ appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s\n");
+ appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
+ appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ NULL, "c.relname", "s.srvname", NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of foreign tables");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
/*
* printACLColumn
*
/* \deu */
extern bool listUserMappings(const char *pattern, bool verbose);
+/* \det */
+extern bool listForeignTables(const char *pattern, bool verbose);
+
#endif /* DESCRIBE_H */
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"));
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;
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));
#define RELKIND_TOASTVALUE 't' /* moved off huge values */
#define RELKIND_VIEW 'v' /* view */
#define RELKIND_COMPOSITE_TYPE 'c' /* composite type */
+#define RELKIND_FOREIGN_TABLE 'f' /* foreign table */
#endif /* PG_CLASS_H */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * pg_foreign_table.h
+ * definition of the system "foreign table" relation (pg_foreign_table)
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_FOREIGN_TABLE_H
+#define PG_FOREIGN_TABLE_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_foreign_table definition. cpp turns this into
+ * typedef struct FormData_pg_foreign_table
+ * ----------------
+ */
+#define ForeignTableRelationId 3118
+
+CATALOG(pg_foreign_table,3118) BKI_WITHOUT_OIDS
+{
+ Oid ftrelid; /* OID of foreign table */
+ Oid ftserver; /* OID of foreign server */
+ text ftoptions[1]; /* FDW-specific options */
+} FormData_pg_foreign_table;
+
+/* ----------------
+ * Form_pg_foreign_table corresponds to a pointer to a tuple with
+ * the format of pg_foreign_table relation.
+ * ----------------
+ */
+typedef FormData_pg_foreign_table *Form_pg_foreign_table;
+
+/* ----------------
+ * compiler constants for pg_foreign_table
+ * ----------------
+ */
+
+#define Natts_pg_foreign_table 3
+#define Anum_pg_foreign_table_ftrelid 1
+#define Anum_pg_foreign_table_ftserver 2
+#define Anum_pg_foreign_table_ftoptions 3
+
+#endif /* PG_FOREIGN_TABLE_H */
extern void AlterUserMapping(AlterUserMappingStmt *stmt);
extern void RemoveUserMapping(DropUserMappingStmt *stmt);
extern void RemoveUserMappingById(Oid umId);
+extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
/* support routines in commands/define.c */
#ifndef FOREIGN_H
#define FOREIGN_H
-#include "nodes/parsenodes.h"
+#include "executor/tuptable.h"
+#include "utils/relcache.h"
/* Helper for obtaining username for user mapping */
{
ServerOpt = 1, /* options applicable to SERVER */
UserMappingOpt = 2, /* options for USER MAPPING */
- FdwOpt = 4 /* options for FOREIGN DATA WRAPPER */
+ FdwOpt = 4, /* options for FOREIGN DATA WRAPPER */
} GenericOptionFlags;
typedef struct ForeignDataWrapper
List *options; /* useoptions as DefElem list */
} UserMapping;
+typedef struct ForeignTable
+{
+ Oid relid; /* relation Oid */
+ Oid serverid; /* server Oid */
+ List *options; /* ftoptions as DefElem list */
+} ForeignTable;
extern ForeignServer *GetForeignServer(Oid serverid);
extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok);
extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
bool missing_ok);
extern Oid GetForeignDataWrapperOidByName(const char *name, bool missing_ok);
+extern ForeignTable *GetForeignTable(Oid relid);
+
+extern bool is_foreign_table(Oid relid);
+
+/* ALTER FOREIGN TABLE ... OPTIONS (...) handlers */
+extern void ATExecGenericOptions(Relation rel, List *options);
#endif /* FOREIGN_H */
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
T_SecLabelStmt,
+ T_CreateForeignTableStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
CREATE_TABLE_LIKE_INDEXES = 1 << 2,
CREATE_TABLE_LIKE_STORAGE = 1 << 3,
CREATE_TABLE_LIKE_COMMENTS = 1 << 4,
- CREATE_TABLE_LIKE_ALL = 0x7FFFFFFF
+ CREATE_TABLE_LIKE_ALL = 0x7FFFFFFF,
+ /* FOREIGN TABLE supports only DEFALUT/CONSTRAINT/COMMENT. */
+ CREATE_TABLE_LIKE_FOREIGN_ALL = ( CREATE_TABLE_LIKE_DEFAULTS |
+ CREATE_TABLE_LIKE_CONSTRAINTS |
+ CREATE_TABLE_LIKE_COMMENTS)
} CreateStmtLikeOption;
/*
OBJECT_DOMAIN,
OBJECT_FDW,
OBJECT_FOREIGN_SERVER,
+ OBJECT_FOREIGN_TABLE,
OBJECT_FUNCTION,
OBJECT_INDEX,
OBJECT_LANGUAGE,
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 */
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 */
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
* ----------------------
#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)
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;
FOREIGNDATAWRAPPEROID,
FOREIGNSERVERNAME,
FOREIGNSERVEROID,
+ FOREIGNTABLEREL,
INDEXRELID,
LANGNAME,
LANGOID,
-- 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
(0 rows)
alter table myview drop d;
-ERROR: "myview" is not a table or composite type
+ERROR: "myview" is not a table or composite type or foreign table
drop view myview;
-- test some commands to make sure they fail on the dropped column
analyze atacc1(a);
CREATE ROLE regress_test_role_super SUPERUSER;
CREATE ROLE regress_test_indirect;
CREATE ROLE unprivileged_role;
+CREATE SCHEMA foreign_schema;
CREATE FOREIGN DATA WRAPPER dummy;
CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
-- At this point we should have 2 built-in wrappers and no servers.
t1 | regress_test_role
(8 rows)
+-- CREATE FOREIGN TABLE
+CREATE TABLE t1 (
+ c1 integer NOT NULL,
+ c2 text DEFAULT 'foo',
+ c3 date CHECK (c3 > '2000-01-01'::date),
+ CONSTRAINT t1_c1_check CHECK (c1 > 0)
+);
+CREATE TABLE t2 ();
+CREATE TABLE f1
+CREATE FOREIGN TABLE ft1 (); -- ERROR
+ERROR: syntax error at or near "CREATE"
+LINE 2: CREATE FOREIGN TABLE ft1 ();
+ ^
+CREATE FOREIGN TABLE ft1 () SERVER no_server; -- ERROR
+ERROR: server "no_server" does not exist
+CREATE FOREIGN TABLE ft1 () INHERITS () SERVER s4; -- ERROR
+ERROR: syntax error at or near ")"
+LINE 1: CREATE FOREIGN TABLE ft1 () INHERITS () SERVER s4;
+ ^
+CREATE FOREIGN TABLE ft1 () INHERITS (no_table) SERVER s4; -- ERROR
+ERROR: relation "no_table" does not exist
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER s4; -- ERROR
+NOTICE: CREATE TABLE will create implicit sequence "ft1_c1_seq" for serial column "ft1.c1"
+ERROR: referenced relation "ft1" is not a table
+CREATE FOREIGN TABLE ft1 (
+ c1 integer NOT NULL,
+ c2 text DEFAULT 'foo',
+ c3 date CHECK (c3 > '2000-01-01'::date),
+ CONSTRAINT ft1_c1_check CHECK (c1 > 0)
+) SERVER s4 OPTIONS (opt1 'val1', opt2 'val2');
+CREATE RULE ft1_insert_rule AS ON INSERT TO ft1 DO INSTEAD
+ INSERT INTO t1 VALUES (new.c1, new.c2, new.c3);
+\d+ ft1
+ Foreign table "public.ft1"
+ Column | Type | Modifiers | Storage | Description
+--------+---------+---------------------+----------+-------------
+ c1 | integer | not null | plain |
+ c2 | text | default 'foo'::text | extended |
+ c3 | date | | plain |
+Check constraints:
+ "ft1_c1_check" CHECK (c1 > 0)
+ "ft1_c3_check" CHECK (c3 > '01-01-2000'::date)
+Rules:
+ ft1_insert_rule AS
+ ON INSERT TO ft1 DO INSTEAD INSERT INTO t1 (c1, c2, c3)
+ VALUES (new.c1, new.c2, new.c3)
+Server: s4
+Has OIDs: no
+
+CREATE FOREIGN TABLE ft2 () INHERITS (t1) SERVER s4 WITH OIDS OPTIONS (opt1 'val1', opt2 'val2');
+\d+ ft2
+ Foreign table "public.ft2"
+ Column | Type | Modifiers | Storage | Description
+--------+---------+---------------------+----------+-------------
+ c1 | integer | not null | plain |
+ c2 | text | default 'foo'::text | extended |
+ c3 | date | | plain |
+Check constraints:
+ "t1_c1_check" CHECK (c1 > 0)
+ "t1_c3_check" CHECK (c3 > '01-01-2000'::date)
+Server: s4
+Inherits: t1
+Has OIDs: yes
+
+\det+
+ List of foreign tables
+ Table | Server | Options
+-------+--------+-----------------------
+ ft1 | s4 | {opt1=val1,opt2=val2}
+ ft2 | s4 | {opt1=val1,opt2=val2}
+(2 rows)
+
+CREATE INDEX id_ft1_c2 ON ft1 (c2); -- ERROR
+ERROR: "ft1" is not a table
+-- ALTER FOREIGN TABLE
+COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
+COMMENT ON FOREIGN TABLE ft1 IS NULL;
+COMMENT ON COLUMN ft1.c1 IS 'foreign column';
+COMMENT ON COLUMN ft1.c1 IS NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
+ERROR: syntax error at or near "using"
+LINE 1: ...R FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0';
+ ^
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR
+ERROR: constraint "no_const" of relation "ft1" does not exist
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
+NOTICE: constraint "no_const" of relation "ft1" does not exist, skipping
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ALTER FOREIGN TABLE ft1 SET WITH OIDS;
+ALTER FOREIGN TABLE ft1 SET WITHOUT OIDS;
+ALTER FOREIGN TABLE ft1 ENABLE RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 ENABLE ALWAYS RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 ENABLE REPLICA RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 DISABLE RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 INHERIT t2;
+ALTER FOREIGN TABLE ft1 NO INHERIT t2;
+ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
+ALTER FOREIGN TABLE ft1 OPTIONS (DROP opt1, SET opt2 'VAL2', ADD opt3 'val3');
+ALTER FOREIGN TABLE ft1 DROP COLUMN no_column; -- ERROR
+ERROR: column "no_column" of relation "ft1" does not exist
+ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column;
+NOTICE: column "no_column" of relation "ft1" does not exist, skipping
+ALTER FOREIGN TABLE ft1 DROP COLUMN c9;
+ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema;
+ALTER FOREIGN TABLE ft1 SET TABLESPACE ts; -- ERROR
+ERROR: syntax error at or near "TABLESPACE"
+LINE 1: ALTER FOREIGN TABLE ft1 SET TABLESPACE ts;
+ ^
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
+\d foreign_schema.foreign_table_1
+ Foreign table "foreign_schema.foreign_table_1"
+ Column | Type | Modifiers
+------------------+---------+---------------------
+ foreign_column_1 | integer | not null
+ c2 | text | default 'foo'::text
+ c3 | date |
+ c4 | integer | default 0
+ c5 | integer |
+ c6 | integer | not null
+ c7 | integer |
+ c8 | text |
+ c10 | integer |
+Check constraints:
+ "ft1_c3_check" CHECK (c3 > '01-01-2000'::date)
+Disabled rules:
+ ft1_insert_rule AS
+ ON INSERT TO foreign_schema.foreign_table_1 DO INSTEAD INSERT INTO t1 (c1, c2, c3)
+ VALUES (new.foreign_column_1, new.c2, new.c3)
+Server: s4
+
+-- DROP FOREIGN TABLE
+DROP FOREIGN TABLE no_table; -- ERROR
+ERROR: foreign table "no_table" does not exist
+DROP FOREIGN TABLE IF EXISTS no_table;
+NOTICE: foreign table "no_table" does not exist, skipping
+DROP FOREIGN TABLE foreign_schema.foreign_table_1;
-- Information schema
SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
foreign_data_wrapper_catalog | foreign_data_wrapper_name | authorization_identifier | library_name | foreign_data_wrapper_language
foreign_data_user | regress_test_role2 | regression | | s6 | FOREIGN SERVER | USAGE | YES
(4 rows)
+SELECT * FROM information_schema.foreign_tables ORDER BY 1, 2, 3;
+ foreign_table_catalog | foreign_table_schema | foreign_table_name | foreign_server_catalog | foreign_server_name
+-----------------------+----------------------+--------------------+------------------------+---------------------
+ regression | public | ft2 | regression | s4
+(1 row)
+
+SELECT * FROM information_schema.foreign_table_options ORDER BY 1, 2, 3, 4;
+ foreign_table_catalog | foreign_table_schema | foreign_table_name | option_name | option_value
+-----------------------+----------------------+--------------------+-------------+--------------
+ regression | public | ft2 | opt1 | val1
+ regression | public | ft2 | opt2 | val2
+(2 rows)
+
SET ROLE regress_test_role;
SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4;
authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value
ERROR: must be owner of foreign server s9
RESET ROLE;
-- Cleanup
+DROP SCHEMA foreign_schema CASCADE;
+DROP TABLE t1 CASCADE;
+NOTICE: drop cascades to foreign table ft2
+DROP TABLE t2 CASCADE;
DROP ROLE regress_test_role; -- ERROR
ERROR: role "regress_test_role" cannot be dropped because some objects depend on it
DETAIL: privileges for server s4
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
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(143 rows)
+(144 rows)
--
-- another sanity check: every system catalog that has OIDs should have
-- 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)
CREATE ROLE regress_test_indirect;
CREATE ROLE unprivileged_role;
+CREATE SCHEMA foreign_schema;
+
CREATE FOREIGN DATA WRAPPER dummy;
CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
DROP SERVER s7;
\deu
+-- CREATE FOREIGN TABLE
+CREATE TABLE t1 (
+ c1 integer NOT NULL,
+ c2 text DEFAULT 'foo',
+ c3 date CHECK (c3 > '2000-01-01'::date),
+ CONSTRAINT t1_c1_check CHECK (c1 > 0)
+);
+CREATE TABLE t2 ();
+CREATE TABLE f1
+CREATE FOREIGN TABLE ft1 (); -- ERROR
+CREATE FOREIGN TABLE ft1 () SERVER no_server; -- ERROR
+CREATE FOREIGN TABLE ft1 () INHERITS () SERVER s4; -- ERROR
+CREATE FOREIGN TABLE ft1 () INHERITS (no_table) SERVER s4; -- ERROR
+CREATE FOREIGN TABLE ft1 (c1 serial) SERVER s4; -- ERROR
+CREATE FOREIGN TABLE ft1 (
+ c1 integer NOT NULL,
+ c2 text DEFAULT 'foo',
+ c3 date CHECK (c3 > '2000-01-01'::date),
+ CONSTRAINT ft1_c1_check CHECK (c1 > 0)
+) SERVER s4 OPTIONS (opt1 'val1', opt2 'val2');
+CREATE RULE ft1_insert_rule AS ON INSERT TO ft1 DO INSTEAD
+ INSERT INTO t1 VALUES (new.c1, new.c2, new.c3);
+\d+ ft1
+CREATE FOREIGN TABLE ft2 () INHERITS (t1) SERVER s4 WITH OIDS OPTIONS (opt1 'val1', opt2 'val2');
+\d+ ft2
+\det+
+CREATE INDEX id_ft1_c2 ON ft1 (c2); -- ERROR
+
+-- ALTER FOREIGN TABLE
+COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
+COMMENT ON FOREIGN TABLE ft1 IS NULL;
+COMMENT ON COLUMN ft1.c1 IS 'foreign column';
+COMMENT ON COLUMN ft1.c1 IS NULL;
+
+ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
+
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c6 SET NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ALTER FOREIGN TABLE ft1 SET WITH OIDS;
+ALTER FOREIGN TABLE ft1 SET WITHOUT OIDS;
+ALTER FOREIGN TABLE ft1 ENABLE RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 ENABLE ALWAYS RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 ENABLE REPLICA RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 DISABLE RULE ft1_insert_rule;
+ALTER FOREIGN TABLE ft1 INHERIT t2;
+ALTER FOREIGN TABLE ft1 NO INHERIT t2;
+ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
+ALTER FOREIGN TABLE ft1 OPTIONS (DROP opt1, SET opt2 'VAL2', ADD opt3 'val3');
+ALTER FOREIGN TABLE ft1 DROP COLUMN no_column; -- ERROR
+ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column;
+ALTER FOREIGN TABLE ft1 DROP COLUMN c9;
+ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema;
+ALTER FOREIGN TABLE ft1 SET TABLESPACE ts; -- ERROR
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
+ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
+\d foreign_schema.foreign_table_1
+
+-- DROP FOREIGN TABLE
+DROP FOREIGN TABLE no_table; -- ERROR
+DROP FOREIGN TABLE IF EXISTS no_table;
+DROP FOREIGN TABLE foreign_schema.foreign_table_1;
+
-- Information schema
SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
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;
RESET ROLE;
-- Cleanup
+DROP SCHEMA foreign_schema CASCADE;
+DROP TABLE t1 CASCADE;
+DROP TABLE t2 CASCADE;
DROP ROLE regress_test_role; -- ERROR
DROP SERVER s5 CASCADE;
DROP SERVER t1 CASCADE;
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.