Re: inherit support for foreign tables - Mailing list pgsql-hackers
| From | Kyotaro HORIGUCHI |
|---|---|
| Subject | Re: inherit support for foreign tables |
| Date | |
| Msg-id | 20140620.170406.123403564.horiguchi.kyotaro@lab.ntt.co.jp Whole thread Raw |
| In response to | Re: inherit support for foreign tables (Etsuro Fujita <fujita.etsuro@lab.ntt.co.jp>) |
| Responses |
Re: inherit support for foreign tables
Re: inherit support for foreign tables |
| List | pgsql-hackers |
Hello,
Before continueing discussion, I tried this patch on the current
head.
This patch applies cleanly except one hunk because of a
modification just AFTER it, which did no harm. Finally all
regression tests suceeded.
Attached is the rebased patch of v11 up to the current master.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 5a4d5aa..5a9aec0 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -115,6 +115,11 @@ static void fileGetForeignRelSize(PlannerInfo *root,static void fileGetForeignPaths(PlannerInfo
*root, RelOptInfo *baserel, Oid foreigntableid);
+static ForeignPath *fileReparameterizeForeignPath(PlannerInfo *root,
+ RelOptInfo *baserel,
+ Oid foreigntableid,
+ ForeignPath *path,
+ Relids required_outer);static ForeignScan
*fileGetForeignPlan(PlannerInfo*root, RelOptInfo *baserel, Oid foreigntableid,
@@ -143,7 +148,7 @@ static bool check_selective_binary_conversion(RelOptInfo *baserel,static void
estimate_size(PlannerInfo*root, RelOptInfo *baserel, FileFdwPlanState *fdw_private);static void
estimate_costs(PlannerInfo*root, RelOptInfo *baserel,
- FileFdwPlanState *fdw_private,
+ FileFdwPlanState *fdw_private, List *join_conds, Cost *startup_cost, Cost
*total_cost);staticint file_acquire_sample_rows(Relation onerel, int elevel, HeapTuple *rows,
inttargrows,
@@ -161,6 +166,7 @@ file_fdw_handler(PG_FUNCTION_ARGS) fdwroutine->GetForeignRelSize = fileGetForeignRelSize;
fdwroutine->GetForeignPaths= fileGetForeignPaths;
+ fdwroutine->ReparameterizeForeignPath = fileReparameterizeForeignPath; fdwroutine->GetForeignPlan =
fileGetForeignPlan; fdwroutine->ExplainForeignScan = fileExplainForeignScan; fdwroutine->BeginForeignScan =
fileBeginForeignScan;
@@ -509,7 +515,8 @@ fileGetForeignPaths(PlannerInfo *root, (Node *) columns));
/* Estimate costs */
- estimate_costs(root, baserel, fdw_private,
+ estimate_costs(root, baserel,
+ fdw_private, NIL, &startup_cost, &total_cost); /*
@@ -534,6 +541,41 @@ fileGetForeignPaths(PlannerInfo *root,}/*
+ * fileReparameterizeForeignPath
+ * Attempt to modify a given path to have greater parameterization
+ */
+static ForeignPath *
+fileReparameterizeForeignPath(PlannerInfo *root,
+ RelOptInfo *baserel,
+ Oid foreigntableid,
+ ForeignPath *path,
+ Relids required_outer)
+{
+ ParamPathInfo *param_info;
+ FileFdwPlanState *fdw_private = (FileFdwPlanState *) baserel->fdw_private;
+ Cost startup_cost;
+ Cost total_cost;
+
+ /* Get the ParamPathInfo */
+ param_info = get_baserel_parampathinfo(root, baserel, required_outer);
+
+ /* Redo the cost estimates */
+ estimate_costs(root, baserel,
+ fdw_private,
+ param_info->ppi_clauses,
+ &startup_cost, &total_cost);
+
+ /* Make and return the new path */
+ return create_foreignscan_path(root, baserel,
+ param_info->ppi_rows,
+ startup_cost,
+ total_cost,
+ NIL, /* no pathkeys */
+ required_outer,
+ path->fdw_private);
+}
+
+/* * fileGetForeignPlan * Create a ForeignScan plan node for scanning the foreign table */
@@ -962,12 +1004,13 @@ estimate_size(PlannerInfo *root, RelOptInfo *baserel, */static voidestimate_costs(PlannerInfo
*root,RelOptInfo *baserel,
- FileFdwPlanState *fdw_private,
+ FileFdwPlanState *fdw_private, List *join_conds, Cost *startup_cost, Cost *total_cost){
BlockNumber pages = fdw_private->pages; double ntuples = fdw_private->ntuples; Cost run_cost = 0;
+ QualCost join_cost; Cost cpu_per_tuple; /*
@@ -978,8 +1021,11 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel, */ run_cost += seq_page_cost *
pages;
- *startup_cost = baserel->baserestrictcost.startup;
- cpu_per_tuple = cpu_tuple_cost * 10 + baserel->baserestrictcost.per_tuple;
+ cost_qual_eval(&join_cost, join_conds, root);
+ *startup_cost =
+ (baserel->baserestrictcost.startup + join_cost.startup);
+ cpu_per_tuple = cpu_tuple_cost * 10 +
+ (baserel->baserestrictcost.per_tuple + join_cost.per_tuple); run_cost += cpu_per_tuple * ntuples;
*total_cost= *startup_cost + run_cost;}
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 7dd43a9..3f7f7c2 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -239,6 +239,11 @@ static void postgresGetForeignRelSize(PlannerInfo *root,static void
postgresGetForeignPaths(PlannerInfo*root, RelOptInfo *baserel, Oid
foreigntableid);
+static ForeignPath *postgresReparameterizeForeignPath(PlannerInfo *root,
+ RelOptInfo *baserel,
+ Oid foreigntableid,
+ ForeignPath *path,
+ Relids required_outer);static ForeignScan
*postgresGetForeignPlan(PlannerInfo*root, RelOptInfo *baserel, Oid
foreigntableid,
@@ -340,6 +345,7 @@ postgres_fdw_handler(PG_FUNCTION_ARGS) /* Functions for scanning foreign tables */
routine->GetForeignRelSize= postgresGetForeignRelSize; routine->GetForeignPaths = postgresGetForeignPaths;
+ routine->ReparameterizeForeignPath = postgresReparameterizeForeignPath; routine->GetForeignPlan =
postgresGetForeignPlan; routine->BeginForeignScan = postgresBeginForeignScan; routine->IterateForeignScan =
postgresIterateForeignScan;
@@ -727,6 +733,48 @@ postgresGetForeignPaths(PlannerInfo *root,}/*
+ * postgresReparameterizeForeignPath
+ * Attempt to modify a given path to have greater parameterization
+ */
+static ForeignPath *
+postgresReparameterizeForeignPath(PlannerInfo *root,
+ RelOptInfo *baserel,
+ Oid foreigntableid,
+ ForeignPath *path,
+ Relids required_outer)
+{
+ ParamPathInfo *param_info;
+ double rows;
+ int width;
+ Cost startup_cost;
+ Cost total_cost;
+
+ /* Get the ParamPathInfo */
+ param_info = get_baserel_parampathinfo(root, baserel, required_outer);
+
+ /* Redo the cost estimates */
+ estimate_path_cost_size(root, baserel,
+ param_info->ppi_clauses,
+ &rows, &width,
+ &startup_cost, &total_cost);
+
+ /*
+ * ppi_rows currently won't get looked at by anything, but still we
+ * may as well ensure that it matches our idea of the rowcount.
+ */
+ param_info->ppi_rows = rows;
+
+ /* Make and return the new path */
+ return create_foreignscan_path(root, baserel,
+ rows,
+ startup_cost,
+ total_cost,
+ NIL, /* no pathkeys */
+ required_outer,
+ NIL); /* no fdw_private list */
+}
+
+/* * postgresGetForeignPlan * Create ForeignScan plan node which implements selected best path */
@@ -1773,11 +1821,8 @@ estimate_path_cost_size(PlannerInfo *root, } else {
- /*
- * We don't support join conditions in this mode (hence, no
- * parameterized paths can be made).
- */
- Assert(join_conds == NIL);
+ Selectivity join_sel;
+ QualCost join_cost; /* Use rows/width estimates made by set_baserel_size_estimates. */ rows =
baserel->rows;
@@ -1790,17 +1835,29 @@ estimate_path_cost_size(PlannerInfo *root, retrieved_rows = clamp_row_est(rows /
fpinfo->local_conds_sel); retrieved_rows = Min(retrieved_rows, baserel->tuples);
+ /* Factor in the selectivity of the join_conds */
+ join_sel = clauselist_selectivity(root,
+ join_conds,
+ baserel->relid,
+ JOIN_INNER,
+ NULL);
+
+ rows = clamp_row_est(rows * join_sel);
+ /* * Cost as though this were a seqscan, which is pessimistic. We
- * effectively imagine the local_conds are being evaluated remotely,
- * too.
+ * effectively imagine the local_conds and join_conds are being
+ * evaluated remotely, too. */ startup_cost = 0; run_cost = 0; run_cost +=
seq_page_cost* baserel->pages;
- startup_cost += baserel->baserestrictcost.startup;
- cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
+ cost_qual_eval(&join_cost, join_conds, root);
+ startup_cost +=
+ (baserel->baserestrictcost.startup + join_cost.startup);
+ cpu_per_tuple = cpu_tuple_cost +
+ (baserel->baserestrictcost.per_tuple + join_cost.per_tuple); run_cost += cpu_per_tuple *
baserel->tuples; total_cost = startup_cost + run_cost;
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 6b5c8b7..ec1492a 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -162,6 +162,37 @@ GetForeignPaths (PlannerInfo *root, <para><programlisting>
+ForeignPath *
+ReparameterizeForeignPath (PlannerInfo *root,
+ RelOptInfo *baserel,
+ Oid foreigntableid,
+ ForeignPath *path,
+ Relids required_outer);
+</programlisting>
+
+ Create an access path for a scan on a foreign table using join
+ clauses, which is called a <quote>parameterized path</>.
+ This is called during query planning.
+ The parameters are as for <function>GetForeignRelSize</>, plus
+ the <structname>ForeignPath</> (previously produced by
+ <function>GetForeignPaths</>), and the IDs of all other tables
+ that provide the join clauses.
+ </para>
+
+ <para>
+ This function must generate a parameterized path for a scan on the
+ foreign table. The parameterized path must contain cost estimates,
+ and can contain any FDW-private information that is needed to identify
+ the specific scan method intended. Unlike the other scan-related
+ functions, this function is optional.
+ </para>
+
+ <para>
+ See <xref linkend="fdw-planning"> for additional information.
+ </para>
+
+ <para>
+<programlisting>ForeignScan *GetForeignPlan (PlannerInfo *root, RelOptInfo *baserel,
@@ -808,10 +839,10 @@ GetForeignServerByName(const char *name, bool missing_ok); <para> The FDW callback
functions<function>GetForeignRelSize</>,
- <function>GetForeignPaths</>, <function>GetForeignPlan</>, and
- <function>PlanForeignModify</> must fit into the workings of the
- <productname>PostgreSQL</> planner. Here are some notes about what
- they must do.
+ <function>GetForeignPaths</>, <function>ReparameterizeForeignPath</>,
+ <function>GetForeignPlan</>, and <function>PlanForeignModify</> must fit
+ into the workings of the <productname>PostgreSQL</> planner. Here are
+ some notes about what they must do. </para> <para>
@@ -841,14 +872,17 @@ GetForeignServerByName(const char *name, bool missing_ok); to initialize it to NULL when the
<literal>baserel</>node is created. It is useful for passing information forward from
<function>GetForeignRelSize</>to <function>GetForeignPaths</> and/or
- <function>GetForeignPaths</> to <function>GetForeignPlan</>, thereby
- avoiding recalculation.
+ <function>GetForeignPaths</> to <function>ReparameterizeForeignPath</>
+ and/or
+ <function>GetForeignPaths</> or <function>ReparameterizeForeignPath</>
+ to <function>GetForeignPlan</>, thereby avoiding recalculation. </para> <para>
- <function>GetForeignPaths</> can identify the meaning of different
- access paths by storing private information in the
- <structfield>fdw_private</> field of <structname>ForeignPath</> nodes.
+ <function>GetForeignPaths</> or <function>ReparameterizeForeignPath</>
+ can identify the meaning of different access paths by storing private
+ information in the <structfield>fdw_private</> field of
+ <structname>ForeignPath</> nodes. <structfield>fdw_private</> is declared as a <type>List</> pointer, but
couldactually contain anything since the core planner does not touch it. However, best practice is to use a
representationthat's dumpable
@@ -891,10 +925,11 @@ GetForeignServerByName(const char *name, bool missing_ok); <replaceable>sub_expression</>,
whichit determines can be executed on the remote server given the locally-evaluated value of the
<replaceable>sub_expression</>. The actual identification of such a
- clause should happen during <function>GetForeignPaths</>, since it would
- affect the cost estimate for the path. The path's
- <structfield>fdw_private</> field would probably include a pointer to
- the identified clause's <structname>RestrictInfo</> node. Then
+ clause should happen during <function>GetForeignPaths</> or
+ <function>ReparameterizeForeignPath</>, since it would affect the cost
+ estimate for the path. The path's <structfield>fdw_private</> field
+ would probably include a pointer to the identified clause's
+ <structname>RestrictInfo</> node. Then <function>GetForeignPlan</> would remove that clause from
<literal>scan_clauses</>, but add the <replaceable>sub_expression</> to <structfield>fdw_exprs</> to ensure that
itgets massaged into executable form. It would probably
diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml
index 4d8cfc5..10461d5 100644
--- a/doc/src/sgml/ref/alter_foreign_table.sgml
+++ b/doc/src/sgml/ref/alter_foreign_table.sgml
@@ -42,6 +42,10 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab ALTER [ COLUMN ]
<replaceableclass="PARAMETER">column_name</replaceable> SET ( <replaceable
class="PARAMETER">attribute_option</replaceable>= <replaceable class="PARAMETER">value</replaceable> [, ... ] )
ALTER[ COLUMN ] <replaceable class="PARAMETER">column_name</replaceable> RESET ( <replaceable
class="PARAMETER">attribute_option</replaceable>[, ... ] ) ALTER [ COLUMN ] <replaceable
class="PARAMETER">column_name</replaceable>OPTIONS ( [ ADD | SET | DROP ] <replaceable
class="PARAMETER">option</replaceable>['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
+ ADD <replaceable class="PARAMETER">table_constraint</replaceable>
+ DROP CONSTRAINT [ IF EXISTS ] <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE
]
+ INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
+ NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable> OWNER TO <replaceable
class="PARAMETER">new_owner</replaceable> OPTIONS ( [ ADD | SET | DROP ] <replaceable
class="PARAMETER">option</replaceable>['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])</synopsis>
@@ -149,6 +153,50 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab </varlistentry>
<varlistentry>
+ <term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable></literal></term>
+ <listitem>
+ <para>
+ This form adds a new constraint to a table using the same syntax as
+ <xref linkend="SQL-CREATEFOREIGNTABLE">.
+ Unlike the case when adding a constraint to a regular table, nothing happens
+ to the underlying storage: this action simply declares that
+ some new constraint holds for all rows in the foreign table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DROP CONSTRAINT [ IF EXISTS ]</literal></term>
+ <listitem>
+ <para>
+ This form drops the specified constraint on a table.
+ If <literal>IF EXISTS</literal> is specified and the constraint
+ does not exist, no error is thrown. In this case a notice is issued instead.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term>
+ <listitem>
+ <para>
+ This form adds the target foreign table as a new child of the specified
+ parent table. The parent table must be an ordinary table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term>
+ <listitem>
+ <para>
+ This form removes the target foreign table from the list of children of
+ the specified parent table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry> <term><literal>OWNER</literal></term> <listitem> <para>
@@ -270,6 +318,24 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab
</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>
@@ -290,6 +356,16 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab
</varlistentry> <varlistentry>
+ <term><replaceable class="PARAMETER">parent_table</replaceable></term>
+ <listitem>
+ <para>
+ A parent table to associate or de-associate with this foreign table.
+ The parent table must be an ordinary table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry> <term><replaceable class="PARAMETER">new_owner</replaceable></term> <listitem>
<para>
@@ -319,10 +395,10 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab <para>
Consistencywith the foreign server is not checked when a column is added or removed with <literal>ADD
COLUMN</literal>or
- <literal>DROP COLUMN</literal>, a <literal>NOT NULL</> constraint is
- added, or a column type is changed with <literal>SET DATA TYPE</>. It is
- the user's responsibility to ensure that the table definition matches the
- remote side.
+ <literal>DROP COLUMN</literal>, a <literal>NOT NULL</> constraint or
+ <literal>CHECK</> constraint is added, or a column type is changed with
+ <literal>SET DATA TYPE</>. It is the user's responsibility to ensure that
+ the table definition matches the remote side. </para> <para>
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 69a1e14..eb1352e 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -967,6 +967,13 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> </para> <para>
+ If a table has any descendant tables that are foreign, a recursive
+ <literal>SET STORAGE</literal> operation will be rejected since it
+ is not permitted to add an <literal>oid</literal> system column to
+ foreign tables.
+ </para>
+
+ <para> The <literal>TRIGGER</>, <literal>CLUSTER</>, <literal>OWNER</>, and <literal>TABLESPACE</> actions
neverrecurse to descendant tables; that is, they always act as though <literal>ONLY</> were specified.
@@ -975,6 +982,19 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> </para> <para>
+ When adding a <literal>CHECK</> constraint with the <literal>NOT VALID
+ </literal> option recursively, an inherited constraint on a descendant
+ table that is foreign will be marked valid without checking
+ consistency with the foreign server.
+ </para>
+
+ <para>
+ A recursive <literal>SET STORAGE</literal> operation will make the
+ storage mode for a descendant table's column unchanged if the table is
+ foreign.
+ </para>
+
+ <para> Changing any part of a system catalog table is not permitted. </para>
diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml
index 08d316a..ec257c5 100644
--- a/doc/src/sgml/ref/analyze.sgml
+++ b/doc/src/sgml/ref/analyze.sgml
@@ -200,6 +200,13 @@ ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> [ </para> <para>
+ The inheritance statistics for a parent table that contains one or more
+ children that are foreign tables are collected only when explicitly
+ selected. If the foreign table's wrapper does not support
+ <command>ANALYZE</command>, the command prints a warning and does nothing.
+ </para>
+
+ <para> If the table being analyzed is completely empty, <command>ANALYZE</command> will not record new
statisticsfor that table. Any existing statistics will be retained.
diff --git a/doc/src/sgml/ref/create_foreign_table.sgml b/doc/src/sgml/ref/create_foreign_table.sgml
index 4a8cf38..1755b38 100644
--- a/doc/src/sgml/ref/create_foreign_table.sgml
+++ b/doc/src/sgml/ref/create_foreign_table.sgml
@@ -19,9 +19,11 @@ <refsynopsisdiv><synopsis>CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable
class="PARAMETER">table_name</replaceable>( [
- <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [
OPTIONS( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [,
...] ) ] [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable
class="PARAMETER">column_constraint</replaceable>[ ... ] ]
+ { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [
OPTIONS( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [,
...] ) ] [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable
class="PARAMETER">column_constraint</replaceable>[ ... ] ]
+ | <replaceable>table_constraint</replaceable> } [, ... ]] )
+[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ] SERVER <replaceable
class="parameter">server_name</replaceable>[OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable
class="PARAMETER">value</replaceable>'[, ... ] ) ]
@@ -30,7 +32,13 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name[ CONSTRAINT
<replaceableclass="PARAMETER">constraint_name</replaceable> ]{ NOT NULL | NULL |
+ CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) | DEFAULT <replaceable>default_expr</replaceable>
}
+
+<phrase>and <replaceable class="PARAMETER">table_constraint</replaceable> is:</phrase>
+
+[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
+{ CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) }</synopsis> </refsynopsisdiv>
@@ -138,6 +146,27 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name </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 each row must satisfy.
+ Expressions evaluating to TRUE or UNKNOWN succeed.
+ 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. The system column <literal>tableoid</literal>
+ may be referenced, but not any other system column.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry> <term><literal>DEFAULT <replaceable>default_expr</replaceable></literal></term> <listitem>
@@ -159,6 +188,18 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name </varlistentry>
<varlistentry>
+ <term><replaceable class="PARAMETER">parent_table</replaceable></term>
+ <listitem>
+ <para>
+ The name of an existing table from which the new foreign table
+ automatically inherits all columns. The specified parent table
+ must be an ordinary table. See <xref linkend="ddl-inherit"> for the
+ details of table inheritance.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry> <term><replaceable class="PARAMETER">server_name</replaceable></term> <listitem> <para>
@@ -187,6 +228,24 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name </refsect1>
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Constraints on foreign tables are not enforced on insert or update.
+ Those definitions simply declare the constraints hold for all rows
+ in the foreign tables. It is the user's responsibility to ensure
+ that those definitions match the remote side. Such constraints are
+ used for some kind of query optimization such as constraint exclusion
+ for partitioned tables (see <xref linkend="ddl-partitioning">).
+ </para>
+
+ <para>
+ Since it is not permitted to add an <literal>oid</> system column to
+ foreign tables, the command will be rejected if any parent tables
+ have <literal>oid</> system columns.
+ </para>
+ </refsect1> <refsect1 id="SQL-CREATEFOREIGNTABLE-examples"> <title>Examples</title>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 33eef9f..21d32c5 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2217,6 +2217,12 @@ AddRelationNewConstraints(Relation rel, if (cdef->contype != CONSTR_CHECK)
continue;
+ if (cdef->is_no_inherit &&
+ rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("CHECK constraints on foreign tables cannot be marked NO INHERIT")));
+ if (cdef->raw_expr != NULL) { Assert(cdef->cooked_expr == NULL);
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index c09ca7e..f67273c 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -82,6 +82,7 @@ int default_statistics_target = 100;/* A few variables that don't seem worth passing
aroundas parameters */static MemoryContext anl_context = NULL;
+static VacuumMode vac_mode;static BufferAccessStrategy vac_strategy;
@@ -115,7 +116,10 @@ static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull); * analyze_rel() --
analyzeone relation */void
-analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
+analyze_rel(Oid relid,
+ VacuumStmt *vacstmt,
+ VacuumMode vacmode,
+ BufferAccessStrategy bstrategy){ Relation onerel; int elevel;
@@ -129,6 +133,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) elevel = DEBUG2;
/* Set up static variables */
+ vac_mode = vacmode; vac_strategy = bstrategy; /*
@@ -1452,6 +1457,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,{ List *tableOIDs; Relation
*rels;
+ AcquireSampleRowsFunc *acquirefunc; double *relblocks; double totalblocks; int
numrows,
@@ -1486,6 +1492,8 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, * BlockNumber, so we use double
arithmetic. */ rels = (Relation *) palloc(list_length(tableOIDs) * sizeof(Relation));
+ acquirefunc = (AcquireSampleRowsFunc *) palloc(list_length(tableOIDs)
+ * sizeof(AcquireSampleRowsFunc)); relblocks = (double *)
palloc(list_length(tableOIDs)* sizeof(double)); totalblocks = 0; nrels = 0;
@@ -1507,7 +1515,40 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, } rels[nrels] =
childrel;
- relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel);
+
+ if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ FdwRoutine *fdwroutine;
+ BlockNumber relpages = 0;
+ bool ok = false;
+
+ /* Ignore unless analyzing a specific inheritance tree */
+ if (vac_mode != VAC_MODE_SINGLE)
+ return 0;
+
+ /* Check whether the FDW supports analysis */
+ fdwroutine = GetFdwRoutineForRelation(childrel, false);
+ if (fdwroutine->AnalyzeForeignTable != NULL)
+ ok = fdwroutine->AnalyzeForeignTable(childrel,
+ &acquirefunc[nrels],
+ &relpages);
+ if (!ok)
+ {
+ /* Give up if the FDW doesn't support analysis */
+ ereport(WARNING,
+ (errmsg("skipping analyze of inheritance tree \"%s\" --- cannot analyze foreign table
\"%s\"",
+ RelationGetRelationName(onerel),
+ RelationGetRelationName(childrel))));
+ return 0;
+ }
+ relblocks[nrels] = (double) relpages;
+ }
+ else
+ {
+ acquirefunc[nrels] = acquire_sample_rows;
+ relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel);
+ }
+ totalblocks += relblocks[nrels]; nrels++; }
@@ -1525,6 +1566,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, { Relation childrel =
rels[i]; double childblocks = relblocks[i];
+ AcquireSampleRowsFunc childacquirefunc = acquirefunc[i]; if (childblocks > 0) {
@@ -1540,12 +1582,12 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, tdrows;
/* Fetch a random sample of the child's rows */
- childrows = acquire_sample_rows(childrel,
- elevel,
- rows + numrows,
- childtargrows,
- &trows,
- &tdrows);
+ childrows = childacquirefunc(childrel,
+ elevel,
+ rows + numrows,
+ childtargrows,
+ &trows,
+ &tdrows); /* We may need to convert from child's rowtype
toparent's */ if (childrows > 0 &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 341262b..9e09906 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -310,7 +310,8 @@ static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);static void
ATSimplePermissions(Relationrel, int allowed_targets);static void ATWrongRelkindError(Relation rel, int
allowed_targets);staticvoid ATSimpleRecursion(List **wqueue, Relation rel,
- AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
+ AlterTableCmd *cmd, bool recurse,
+ bool include_foreign, LOCKMODE lockmode);static void ATTypedTableRecursion(List **wqueue, Relation
rel,AlterTableCmd *cmd, LOCKMODE lockmode);static List *find_typed_table_dependencies(Oid typeOid,
constchar *typeName,
@@ -466,10 +467,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("ON COMMIT can only be used on temporary tables")));
- if (stmt->constraints != NIL && relkind == RELKIND_FOREIGN_TABLE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("constraints are not supported on foreign tables"))); /* * Look up the namespace in
whichwe are supposed to create the relation,
@@ -556,6 +553,30 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
stmt->relation->relpersistence, &inheritOids, &old_constraints, &parentOidCount);
+ if (relkind == RELKIND_FOREIGN_TABLE)
+ {
+ /*
+ * Don't allow a foreign table to inherit from parents that have OID
+ * system columns.
+ */
+ if (parentOidCount > 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot inherit from relation with OIDs")));
+
+ /*
+ * Reset the storage parameter for inherited attributes that have
+ * non-default values.
+ */
+ foreach(listptr, schema)
+ {
+ ColumnDef *colDef = lfirst(listptr);
+
+ if (colDef->storage != 0)
+ colDef->storage = 0;
+ }
+ }
+ /* * Create a tuple descriptor from the relation schema. Note that this * deals with column names, types,
andNOT NULL constraints, but not
@@ -3065,24 +3086,28 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, * rules. */
ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
- ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+ /* Recurse to child tables that are foreign, too */
+ ATSimpleRecursion(wqueue, rel, cmd, recurse, true, 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, ATT_TABLE | ATT_FOREIGN_TABLE);
- ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+ /* Recurse to child tables that are foreign, too */
+ ATSimpleRecursion(wqueue, rel, cmd, recurse, true, lockmode); /* No command-specific prep
needed*/ pass = AT_PASS_DROP; break; case AT_SetNotNull: /* ALTER COLUMN SET NOT
NULL*/ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
- ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+ /* Recurse to child tables that are foreign, too */
+ ATSimpleRecursion(wqueue, rel, cmd, recurse, true, lockmode); /* No command-specific prep
needed*/ pass = AT_PASS_ADD_CONSTR; break; case AT_SetStatistics: /* ALTER COLUMN SET
STATISTICS*/
- ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+ /* Recurse to child tables that are foreign, too */
+ ATSimpleRecursion(wqueue, rel, cmd, recurse, true, lockmode); /* Performs own permission checks
*/ ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode); pass = AT_PASS_MISC;
@@ -3095,7 +3120,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, break; case
AT_SetStorage: /* ALTER COLUMN SET STORAGE */ ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
- ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+ /* Don't recurse to child tables that are foreign */
+ ATSimpleRecursion(wqueue, rel, cmd, recurse, false, lockmode); /* No command-specific prep
needed*/ pass = AT_PASS_MISC; break;
@@ -3113,7 +3139,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_ADD_INDEX;
break; case AT_AddConstraint: /* ADD CONSTRAINT */
- ATSimplePermissions(rel, ATT_TABLE);
+ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Recursion occurs during execution
phase*/ /* No command-specific prep needed except saving recurse flag */ if (recurse)
@@ -3127,7 +3153,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_ADD_CONSTR;
break; case AT_DropConstraint: /* DROP CONSTRAINT */
- ATSimplePermissions(rel, ATT_TABLE);
+ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Recursion occurs during execution
phase*/ /* No command-specific prep needed except saving recurse flag */ if (recurse)
@@ -3195,11 +3221,17 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_MISC;
break; case AT_AddInherit: /* INHERIT */
- ATSimplePermissions(rel, ATT_TABLE);
+ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* This command never recurses */
ATPrepAddInherit(rel); pass = AT_PASS_MISC; break;
+ case AT_DropInherit: /* NO INHERIT */
+ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
+ /* This command never recurses */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break; case AT_AlterConstraint: /* ALTER CONSTRAINT */ ATSimplePermissions(rel,
ATT_TABLE); pass = AT_PASS_MISC;
@@ -3233,7 +3265,6 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, case AT_EnableAlwaysRule:
case AT_EnableReplicaRule: case AT_DisableRule:
- case AT_DropInherit: /* NO INHERIT */ case AT_AddOf: /* OF */ case AT_DropOf: /*
NOTOF */ ATSimplePermissions(rel, ATT_TABLE);
@@ -4174,7 +4205,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets) */static voidATSimpleRecursion(List
**wqueue,Relation rel,
- AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
+ AlterTableCmd *cmd, bool recurse,
+ bool include_foreign, LOCKMODE lockmode){ /* * Propagate to children if desired. Non-table
relationsnever have
@@ -4202,8 +4234,12 @@ ATSimpleRecursion(List **wqueue, Relation rel, continue; /*
find_all_inheritorsalready got lock */ childrel = relation_open(childrelid, NoLock);
- CheckTableNotInUse(childrel, "ALTER TABLE");
- ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
+ if (childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE
+ || include_foreign)
+ {
+ CheckTableNotInUse(childrel, "ALTER TABLE");
+ ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
+ } relation_close(childrel, NoLock); } }
@@ -4493,7 +4529,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, /* At top level,
permissioncheck was done in ATPrepCmd, else do it */ if (recursing)
- ATSimplePermissions(rel, ATT_TABLE);
+ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); attrdesc = heap_open(AttributeRelationId,
RowExclusiveLock);
@@ -4789,6 +4825,11 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, /*
find_inheritance_childrenalready got lock */ childrel = heap_open(childrelid, NoLock);
+ if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && isOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot add OID column to foreign table \"%s\"",
+ RelationGetRelationName(childrel)))); CheckTableNotInUse(childrel, "ALTER TABLE");
/* Find or create work queue entry for this table */
@@ -5389,7 +5430,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, /* At top level, permission
checkwas done in ATPrepCmd, else do it */ if (recursing)
- ATSimplePermissions(rel, ATT_TABLE);
+ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* * get the number of the attribute
@@ -5781,7 +5822,14 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, /* At top level,
permissioncheck was done in ATPrepCmd, else do it */ if (recursing)
- ATSimplePermissions(rel, ATT_TABLE);
+ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
+
+ /* Don't allow ADD CONSTRAINT NOT VALID to be applied to foreign tables */
+ if (tab->relkind == RELKIND_FOREIGN_TABLE &&
+ constr->skip_validation && !recursing)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("NOT VALID is not supported on foreign tables"))); /* * Call AddRelationNewConstraints
todo the work, making sure it works on
@@ -5792,9 +5840,17 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, * omitted from the
returnedlist, which is what we want: we do not need * to do any validation work. That can only happen at child
tables, * though, since we disallow merging at the top level.
- */
+ *
+ * When propagating a NOT VALID option to children that are foreign tables,
+ * we quietly ignore the option. Note that this is safe because foreign
+ * tables don't have any children.
+ */
+ constr = (Constraint *) copyObject(constr);
+ if (tab->relkind == RELKIND_FOREIGN_TABLE &&
+ constr->skip_validation && recursing)
+ constr->skip_validation = false; newcons = AddRelationNewConstraints(rel, NIL,
- list_make1(copyObject(constr)),
+ list_make1(constr), recursing,
/*allow_merge */ !recursing, /* is_local */
is_readd); /* is_internal */
@@ -7274,7 +7330,7 @@ ATExecDropConstraint(Relation rel, const char *constrName, /* At top level, permission check
wasdone in ATPrepCmd, else do it */ if (recursing)
- ATSimplePermissions(rel, ATT_TABLE);
+ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); conrel = heap_open(ConstraintRelationId,
RowExclusiveLock);
@@ -7609,7 +7665,10 @@ ATPrepAlterColumnType(List **wqueue, * alter would put them out of step. */ if
(recurse)
- ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+ {
+ /* Recurse to child tables that are foreign, too */
+ ATSimpleRecursion(wqueue, rel, cmd, recurse, true, lockmode);
+ } else if (!recursing && find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
ereport(ERROR,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 3d2c739..b5e3ccf 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -61,6 +61,7 @@ int vacuum_multixact_freeze_table_age;/* A few variables that don't seem worth passing
aroundas parameters */static MemoryContext vac_context = NULL;
+static VacuumMode vac_mode;static BufferAccessStrategy vac_strategy;
@@ -146,6 +147,20 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
ALLOCSET_DEFAULT_MAXSIZE); /*
+ * Identify vacuum mode. If relid is not InvalidOid, the caller should be
+ * an autovacuum worker. See the above comments.
+ */
+ if (relid != InvalidOid)
+ vac_mode = VAC_MODE_AUTOVACUUM;
+ else
+ {
+ if (!vacstmt->relation)
+ vac_mode = VAC_MODE_ALL;
+ else
+ vac_mode = VAC_MODE_SINGLE;
+ }
+
+ /* * If caller didn't give us a buffer strategy object, make one in the * cross-transaction memory
context. */
@@ -248,7 +263,7 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
PushActiveSnapshot(GetTransactionSnapshot()); }
- analyze_rel(relid, vacstmt, vac_strategy);
+ analyze_rel(relid, vacstmt, vac_mode, vac_strategy); if (use_own_xacts)
{
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 0410fdd..836fae6 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1338,11 +1338,12 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) /* *
Buildan RTE for the child, and attach to query's rangetable list. * We copy most fields of the parent's RTE,
butreplace relation OID,
- * and set inh = false. Also, set requiredPerms to zero since all
- * required permissions checks are done on the original RTE.
+ * relkind and set inh = false. Also, set requiredPerms to zero since
+ * all required permissions checks are done on the original RTE. */ childrte = copyObject(rte);
childrte->relid = childOID;
+ childrte->relkind = newrelation->rd_rel->relkind; childrte->inh = false; childrte->requiredPerms
=0; parse->rtable = lappend(parse->rtable, childrte);
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 4e05dcd..880595f 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -16,6 +16,7 @@#include <math.h>
+#include "foreign/fdwapi.h"#include "miscadmin.h"#include "nodes/nodeFuncs.h"#include "optimizer/clauses.h"
@@ -2062,6 +2063,31 @@ reparameterize_path(PlannerInfo *root, Path *path, case T_SubqueryScan: return
create_subqueryscan_path(root,rel, path->pathkeys, required_outer);
+ case T_ForeignScan:
+ {
+ ForeignPath *newpath = NULL;
+
+ /* Let the FDW reparameterize the path node if possible */
+ if (rel->fdwroutine->ReparameterizeForeignPath != NULL)
+ {
+ Index scan_relid = rel->relid;
+ RangeTblEntry *rte;
+
+ /* it should be a base rel... */
+ Assert(scan_relid > 0);
+ Assert(rel->rtekind == RTE_RELATION);
+ rte = planner_rt_fetch(scan_relid, root);
+ Assert(rte->rtekind == RTE_RELATION);
+
+ newpath =
+ rel->fdwroutine->ReparameterizeForeignPath(root,
+ rel,
+ rte->relid,
+ (ForeignPath *) path,
+ required_outer);
+ }
+ return (Path *) newpath;
+ } default: break; }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 605c9b4..a206501 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4213,32 +4213,32 @@ AlterForeignServerStmt: ALTER SERVER name foreign_server_version
alter_generic_oCreateForeignTableStmt: CREATE FOREIGN TABLE qualified_name '(' OptTableElementList
')'
- SERVER name create_generic_options
+ OptInherit SERVER name create_generic_options { CreateForeignTableStmt
*n= makeNode(CreateForeignTableStmt); $4->relpersistence = RELPERSISTENCE_PERMANENT;
n->base.relation = $4; n->base.tableElts = $6;
- n->base.inhRelations = NIL;
+ n->base.inhRelations = $8; n->base.if_not_exists = false; /*
FDW-specificdata */
- n->servername = $9;
- n->options = $10;
+ n->servername = $10;
+ n->options = $11; $$ = (Node *) n; } | CREATE FOREIGN
TABLEIF_P NOT EXISTS qualified_name '(' OptTableElementList ')'
- SERVER name create_generic_options
+ OptInherit SERVER name create_generic_options { CreateForeignTableStmt
*n= makeNode(CreateForeignTableStmt); $7->relpersistence = RELPERSISTENCE_PERMANENT;
n->base.relation = $7; n->base.tableElts = $9;
- n->base.inhRelations = NIL;
+ n->base.inhRelations = $11; n->base.if_not_exists = true; /*
FDW-specificdata */
- n->servername = $12;
- n->options = $13;
+ n->servername = $13;
+ n->options = $14; $$ = (Node *) n; } ;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 7c1939f..949392a 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -515,12 +515,6 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) break;
case CONSTR_CHECK:
- if (cxt->isforeign)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("constraints are not supported on foreign tables"),
- parser_errposition(cxt->pstate,
- constraint->location))); cxt->ckconstraints =
lappend(cxt->ckconstraints,constraint); break;
@@ -529,7 +523,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) if
(cxt->isforeign) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("constraints are not supported on foreign tables"),
+ errmsg("primary key or unique constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate, constraint->location)));
if (constraint->keys == NIL)
@@ -546,7 +540,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) if
(cxt->isforeign) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("constraints are not supported on foreign tables"),
+ errmsg("foreign key constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate, constraint->location)));
@@ -605,10 +599,14 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)static
voidtransformTableConstraint(CreateStmtContext*cxt, Constraint *constraint){
- if (cxt->isforeign)
+ if (cxt->isforeign &&
+ (constraint->contype == CONSTR_PRIMARY ||
+ constraint->contype == CONSTR_UNIQUE ||
+ constraint->contype == CONSTR_EXCLUSION ||
+ constraint->contype == CONSTR_FOREIGN)) ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("constraints are not supported on foreign tables"),
+ errmsg("primary key, unique, exclusion, or foreign key constraints are not supported on foreign
tables"), parser_errposition(cxt->pstate, constraint->location)));
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index d33552a..400e373 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -140,6 +140,15 @@ extern int vacuum_multixact_freeze_min_age;extern int vacuum_multixact_freeze_table_age;
+/* Possible modes for vacuum() */
+typedef enum
+{
+ VAC_MODE_ALL, /* Vacuum/analyze all relations */
+ VAC_MODE_SINGLE, /* Vacuum/analyze a specific relation */
+ VAC_MODE_AUTOVACUUM /* Autovacuum worker */
+} VacuumMode;
+
+/* in commands/vacuum.c */extern void vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, BufferAccessStrategy
bstrategy,bool for_wraparound, bool isTopLevel);
@@ -174,7 +183,9 @@ extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
BufferAccessStrategybstrategy);/* in commands/analyze.c */
-extern void analyze_rel(Oid relid, VacuumStmt *vacstmt,
+extern void analyze_rel(Oid relid,
+ VacuumStmt *vacstmt,
+ VacuumMode vacmode, BufferAccessStrategy bstrategy);extern bool std_typanalyze(VacAttrStats
*stats);externdouble anl_random_fract(void);
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 1b735da..5f3996a 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -31,6 +31,12 @@ typedef void (*GetForeignPaths_function) (PlannerInfo *root,
RelOptInfo *baserel, Oid foreigntableid);
+typedef ForeignPath *(*ReparameterizeForeignPath_function) (PlannerInfo *root,
+ RelOptInfo *baserel,
+ Oid foreigntableid,
+ ForeignPath *path,
+ Relids required_outer);
+typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root,
RelOptInfo *baserel, Oid foreigntableid,
@@ -117,6 +123,7 @@ typedef struct FdwRoutine /* Functions for scanning foreign tables */
GetForeignRelSize_functionGetForeignRelSize; GetForeignPaths_function GetForeignPaths;
+ ReparameterizeForeignPath_function ReparameterizeForeignPath; GetForeignPlan_function GetForeignPlan;
BeginForeignScan_functionBeginForeignScan; IterateForeignScan_function IterateForeignScan;
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index ff203b2..36df338 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -748,16 +748,12 @@ CREATE TABLE use_ft1_column_type (x ft1);ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE
integer; -- ERRORERROR: cannot alter foreign table "ft1" because column "use_ft1_column_type.x" uses its row
typeDROPTABLE use_ft1_column_type;
-ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0); -- ERROR
-ERROR: constraints are not supported on foreign tables
-LINE 1: ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c...
- ^
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const;
-- ERROR
-ERROR: "ft1" is not a table
+ERROR: constraint "no_const" of relation "ft1" does not existALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS
no_const;
-ERROR: "ft1" is not a table
-ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
-ERROR: "ft1" is not a table
+NOTICE: constraint "no_const" of relation "ft1" does not exist, skipping
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c9_check;ALTER FOREIGN TABLE ft1 SET WITH OIDS;
--ERRORERROR: "ft1" is not a tableALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
index 0f0869e..8f9ea86 100644
--- a/src/test/regress/sql/foreign_data.sql
+++ b/src/test/regress/sql/foreign_data.sql
@@ -314,10 +314,10 @@ ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET STATISTICS -1;CREATE TABLE use_ft1_column_type (x
ft1);ALTERFOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERRORDROP TABLE use_ft1_column_type;
-ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0); -- ERROR
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const;
-- ERRORALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
-ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c9_check;ALTER FOREIGN TABLE ft1 SET WITH OIDS;
--ERRORALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote
'~',ADD escape '@');
pgsql-hackers by date: