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);
 static int file_acquire_sample_rows(Relation onerel, int elevel,
 						 HeapTuple *rows, int targrows,
@@ -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 void
 estimate_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
      could actually contain anything since the core planner does not touch
      it.  However, best practice is to use a representation that's dumpable
@@ -891,10 +925,11 @@ GetForeignServerByName(const char *name, bool missing_ok);
      <replaceable>sub_expression</>, which it 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 it gets 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 ] <replaceable class="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>
     Consistency with the foreign server is not checked when a column is added
     or removed with <literal>ADD COLUMN</literal> or
-    <literal>DROP COLUMN</literal>, a <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 never recurse 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 statistics for 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 <replaceable class="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 around as 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() -- analyze one 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 to parent'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(Relation rel, int allowed_targets);
 static void ATWrongRelkindError(Relation rel, int allowed_targets);
 static void ATSimpleRecursion(List **wqueue, Relation rel,
-				  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
+				  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, const char *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 which we 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, and NOT 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: /* NOT OF */
 			ATSimplePermissions(rel, ATT_TABLE);
@@ -4174,7 +4205,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets)
  */
 static void
 ATSimpleRecursion(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 relations never have
@@ -4202,8 +4234,12 @@ ATSimpleRecursion(List **wqueue, Relation rel,
 				continue;
 			/* find_all_inheritors already 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, permission check 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_children already 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 check was 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, permission check 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 to do the work, making sure it works on
@@ -5792,9 +5840,17 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	 * omitted from the returned list, 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 was done 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 around as 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)
 		/*
 		 * Build an RTE for the child, and attach to query's rangetable list.
 		 * We copy most fields of the parent's RTE, but replace 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_o
 CreateForeignTableStmt:
 		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-specific data */
-					n->servername = $9;
-					n->options = $10;
+					n->servername = $10;
+					n->options = $11;
 					$$ = (Node *) n;
 				}
 		| CREATE FOREIGN TABLE IF_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-specific data */
-					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 void
 transformTableConstraint(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,
 				BufferAccessStrategy bstrategy);
 
 /* 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);
 extern double 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_function GetForeignRelSize;
 	GetForeignPaths_function GetForeignPaths;
+	ReparameterizeForeignPath_function ReparameterizeForeignPath;
 	GetForeignPlan_function GetForeignPlan;
 	BeginForeignScan_function BeginForeignScan;
 	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;	-- ERROR
 ERROR:  cannot alter foreign table "ft1" because column "use_ft1_column_type.x" uses its row type
 DROP TABLE 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 exist
 ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
-ERROR:  "ft1" is not a table
-ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
-ERROR:  "ft1" is not a table
+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;                          -- ERROR
 ERROR:  "ft1" is not a table
 ALTER 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);
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer;	-- ERROR
 DROP 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;               -- ERROR
 ALTER 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;                          -- ERROR
 ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
 ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@');
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to