*** a/contrib/file_fdw/file_fdw.c
--- b/contrib/file_fdw/file_fdw.c
***************
*** 115,120 **** static void fileGetForeignRelSize(PlannerInfo *root,
--- 115,125 ----
  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,149 **** 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,
  			   Cost *startup_cost, Cost *total_cost);
  static int file_acquire_sample_rows(Relation onerel, int elevel,
  						 HeapTuple *rows, int targrows,
--- 148,154 ----
  static void estimate_size(PlannerInfo *root, RelOptInfo *baserel,
  			  FileFdwPlanState *fdw_private);
  static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
! 			   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,166 **** file_fdw_handler(PG_FUNCTION_ARGS)
--- 166,172 ----
  
  	fdwroutine->GetForeignRelSize = fileGetForeignRelSize;
  	fdwroutine->GetForeignPaths = fileGetForeignPaths;
+ 	fdwroutine->ReparameterizeForeignPath = fileReparameterizeForeignPath;
  	fdwroutine->GetForeignPlan = fileGetForeignPlan;
  	fdwroutine->ExplainForeignScan = fileExplainForeignScan;
  	fdwroutine->BeginForeignScan = fileBeginForeignScan;
***************
*** 509,515 **** fileGetForeignPaths(PlannerInfo *root,
  										  (Node *) columns));
  
  	/* Estimate costs */
! 	estimate_costs(root, baserel, fdw_private,
  				   &startup_cost, &total_cost);
  
  	/*
--- 515,522 ----
  										  (Node *) columns));
  
  	/* Estimate costs */
! 	estimate_costs(root, baserel,
! 				   fdw_private, NIL,
  				   &startup_cost, &total_cost);
  
  	/*
***************
*** 534,539 **** fileGetForeignPaths(PlannerInfo *root,
--- 541,581 ----
  }
  
  /*
+  * 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);
+ 
+ 	/* Recreate 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
   */
***************
*** 966,977 **** estimate_size(PlannerInfo *root, RelOptInfo *baserel,
   */
  static void
  estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
! 			   FileFdwPlanState *fdw_private,
  			   Cost *startup_cost, Cost *total_cost)
  {
  	BlockNumber pages = fdw_private->pages;
  	double		ntuples = fdw_private->ntuples;
  	Cost		run_cost = 0;
  	Cost		cpu_per_tuple;
  
  	/*
--- 1008,1020 ----
   */
  static void
  estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
! 			   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;
  
  	/*
***************
*** 982,989 **** 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;
  	run_cost += cpu_per_tuple * ntuples;
  	*total_cost = *startup_cost + run_cost;
  }
--- 1025,1035 ----
  	 */
  	run_cost += seq_page_cost * pages;
  
! 	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;
  }
*** a/contrib/file_fdw/input/file_fdw.source
--- b/contrib/file_fdw/input/file_fdw.source
***************
*** 134,139 **** DELETE FROM agg_csv WHERE a = 100;
--- 134,177 ----
  -- but this should be ignored
  SELECT * FROM agg_csv FOR UPDATE;
  
+ -- table inheritance tests
+ CREATE TABLE agg (a int2, b float4);
+ ALTER FOREIGN TABLE agg_text INHERIT agg;
+ ALTER FOREIGN TABLE agg_csv INHERIT agg;
+ INSERT INTO agg
+   SELECT x, 5432.0 FROM generate_series(0,1000) x;
+ SELECT * FROM agg WHERE b < 10.0 ORDER BY a, b;
+ SELECT * FROM ONLY agg WHERE b < 10.0 ORDER BY a, b;
+ SELECT * FROM agg_text;
+ SELECT * FROM agg_csv;
+ -- updates aren't supported
+ UPDATE agg SET a = 1;
+ DELETE FROM agg WHERE a = 100;
+ -- but this should be ignored
+ SELECT * FROM agg WHERE b < 10.0 ORDER BY a, b FOR UPDATE;
+ -- path reparameterization should work
+ CREATE TABLE int_tbl (f1) AS SELECT x FROM generate_series(0,1000) x;
+ ANALYZE int_tbl;
+ ANALYZE agg;
+ ANALYZE agg_text;
+ ANALYZE agg_csv;
+ \t on
+ EXPLAIN (VERBOSE, COSTS FALSE)
+ SELECT * FROM agg JOIN (SELECT f1 FROM int_tbl LIMIT 1) ss ON a = f1;
+ \t off
+ SELECT * FROM agg JOIN (SELECT f1 FROM int_tbl LIMIT 1) ss ON a = f1;
+ CREATE INDEX agg_idx ON agg (a);
+ ANALYZE agg;
+ \t on
+ EXPLAIN (VERBOSE, COSTS FALSE)
+ SELECT * FROM agg JOIN (SELECT f1 FROM int_tbl LIMIT 1) ss ON a = f1;
+ \t off
+ SELECT * FROM agg JOIN (SELECT f1 FROM int_tbl LIMIT 1) ss ON a = f1;
+ ALTER FOREIGN TABLE agg_csv NO INHERIT agg;
+ ALTER FOREIGN TABLE agg_text NO INHERIT agg;
+ DROP TABLE int_tbl;
+ DROP TABLE agg;
+ 
  -- privilege tests
  SET ROLE file_fdw_superuser;
  SELECT * FROM agg_text ORDER BY a;
*** a/contrib/file_fdw/output/file_fdw.source
--- b/contrib/file_fdw/output/file_fdw.source
***************
*** 219,224 **** SELECT * FROM agg_csv FOR UPDATE;
--- 219,348 ----
    42 |  324.78
  (3 rows)
  
+ -- table inheritance tests
+ CREATE TABLE agg (a int2, b float4);
+ ALTER FOREIGN TABLE agg_text INHERIT agg;
+ ALTER FOREIGN TABLE agg_csv INHERIT agg;
+ INSERT INTO agg
+   SELECT x, 5432.0 FROM generate_series(0,1000) x;
+ SELECT * FROM agg WHERE b < 10.0 ORDER BY a, b;
+  a  |    b    
+ ----+---------
+   0 | 0.09561
+   0 | 0.09561
+  56 |     7.8
+ (3 rows)
+ 
+ SELECT * FROM ONLY agg WHERE b < 10.0 ORDER BY a, b;
+  a | b 
+ ---+---
+ (0 rows)
+ 
+ SELECT * FROM agg_text;
+   a  |    b    
+ -----+---------
+   56 |     7.8
+  100 |  99.097
+    0 | 0.09561
+   42 |  324.78
+ (4 rows)
+ 
+ SELECT * FROM agg_csv;
+   a  |    b    
+ -----+---------
+  100 |  99.097
+    0 | 0.09561
+   42 |  324.78
+ (3 rows)
+ 
+ -- updates aren't supported
+ UPDATE agg SET a = 1;
+ ERROR:  cannot update foreign table "agg_text"
+ DELETE FROM agg WHERE a = 100;
+ ERROR:  cannot delete from foreign table "agg_text"
+ -- but this should be ignored
+ SELECT * FROM agg WHERE b < 10.0 ORDER BY a, b FOR UPDATE;
+  a  |    b    
+ ----+---------
+   0 | 0.09561
+   0 | 0.09561
+  56 |     7.8
+ (3 rows)
+ 
+ -- path reparameterization should work
+ CREATE TABLE int_tbl (f1) AS SELECT x FROM generate_series(0,1000) x;
+ ANALYZE int_tbl;
+ ANALYZE agg;
+ ANALYZE agg_text;
+ ANALYZE agg_csv;
+ \t on
+ EXPLAIN (VERBOSE, COSTS FALSE)
+ SELECT * FROM agg JOIN (SELECT f1 FROM int_tbl LIMIT 1) ss ON a = f1;
+  Hash Join
+    Output: agg.a, agg.b, int_tbl.f1
+    Hash Cond: (agg.a = int_tbl.f1)
+    ->  Append
+          ->  Seq Scan on public.agg
+                Output: agg.a, agg.b
+          ->  Foreign Scan on public.agg_text
+                Output: agg_text.a, agg_text.b
+                Foreign File: @abs_srcdir@/data/agg.data
+          ->  Foreign Scan on public.agg_csv
+                Output: agg_csv.a, agg_csv.b
+                Foreign File: @abs_srcdir@/data/agg.csv
+    ->  Hash
+          Output: int_tbl.f1
+          ->  Limit
+                Output: int_tbl.f1
+                ->  Seq Scan on public.int_tbl
+                      Output: int_tbl.f1
+ 
+ \t off
+ SELECT * FROM agg JOIN (SELECT f1 FROM int_tbl LIMIT 1) ss ON a = f1;
+  a |    b    | f1 
+ ---+---------+----
+  0 |    5432 |  0
+  0 | 0.09561 |  0
+  0 | 0.09561 |  0
+ (3 rows)
+ 
+ CREATE INDEX agg_idx ON agg (a);
+ ANALYZE agg;
+ \t on
+ EXPLAIN (VERBOSE, COSTS FALSE)
+ SELECT * FROM agg JOIN (SELECT f1 FROM int_tbl LIMIT 1) ss ON a = f1;
+  Nested Loop
+    Output: agg.a, agg.b, int_tbl.f1
+    ->  Limit
+          Output: int_tbl.f1
+          ->  Seq Scan on public.int_tbl
+                Output: int_tbl.f1
+    ->  Append
+          ->  Index Scan using agg_idx on public.agg
+                Output: agg.a, agg.b
+                Index Cond: (agg.a = int_tbl.f1)
+          ->  Foreign Scan on public.agg_text
+                Output: agg_text.a, agg_text.b
+                Filter: (int_tbl.f1 = agg_text.a)
+                Foreign File: @abs_srcdir@/data/agg.data
+          ->  Foreign Scan on public.agg_csv
+                Output: agg_csv.a, agg_csv.b
+                Filter: (int_tbl.f1 = agg_csv.a)
+                Foreign File: @abs_srcdir@/data/agg.csv
+ 
+ \t off
+ SELECT * FROM agg JOIN (SELECT f1 FROM int_tbl LIMIT 1) ss ON a = f1;
+  a |    b    | f1 
+ ---+---------+----
+  0 |    5432 |  0
+  0 | 0.09561 |  0
+  0 | 0.09561 |  0
+ (3 rows)
+ 
+ ALTER FOREIGN TABLE agg_csv NO INHERIT agg;
+ ALTER FOREIGN TABLE agg_text NO INHERIT agg;
+ DROP TABLE int_tbl;
+ DROP TABLE agg;
  -- privilege tests
  SET ROLE file_fdw_superuser;
  SELECT * FROM agg_text ORDER BY a;
*** a/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/contrib/postgres_fdw/expected/postgres_fdw.out
***************
*** 2835,2840 **** NOTICE:  NEW: (13,"test triggered !")
--- 2835,3373 ----
  (1 row)
  
  -- ===================================================================
+ -- test inheritance features
+ -- ===================================================================
+ CREATE TABLE a (aa TEXT);
+ CREATE TABLE loct (aa TEXT, bb TEXT);
+ CREATE FOREIGN TABLE b (bb TEXT) INHERITS (a)
+   SERVER loopback OPTIONS (table_name 'loct');
+ INSERT INTO a(aa) VALUES('aaa');
+ INSERT INTO a(aa) VALUES('aaaa');
+ INSERT INTO a(aa) VALUES('aaaaa');
+ INSERT INTO a(aa) VALUES('aaaaaa');
+ INSERT INTO a(aa) VALUES('aaaaaaa');
+ INSERT INTO a(aa) VALUES('aaaaaaaa');
+ INSERT INTO b(aa) VALUES('bbb');
+ INSERT INTO b(aa) VALUES('bbbb');
+ INSERT INTO b(aa) VALUES('bbbbb');
+ INSERT INTO b(aa) VALUES('bbbbbb');
+ INSERT INTO b(aa) VALUES('bbbbbbb');
+ INSERT INTO b(aa) VALUES('bbbbbbbb');
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+  relname |    aa    
+ ---------+----------
+  a       | aaa
+  a       | aaaa
+  a       | aaaaa
+  a       | aaaaaa
+  a       | aaaaaaa
+  a       | aaaaaaaa
+  b       | bbb
+  b       | bbbb
+  b       | bbbbb
+  b       | bbbbbb
+  b       | bbbbbbb
+  b       | bbbbbbbb
+ (12 rows)
+ 
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+  relname |    aa    | bb 
+ ---------+----------+----
+  b       | bbb      | 
+  b       | bbbb     | 
+  b       | bbbbb    | 
+  b       | bbbbbb   | 
+  b       | bbbbbbb  | 
+  b       | bbbbbbbb | 
+ (6 rows)
+ 
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+  relname |    aa    
+ ---------+----------
+  a       | aaa
+  a       | aaaa
+  a       | aaaaa
+  a       | aaaaaa
+  a       | aaaaaaa
+  a       | aaaaaaaa
+ (6 rows)
+ 
+ UPDATE a SET aa='zzzzzz' WHERE aa LIKE 'aaa%';
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+  relname |    aa    
+ ---------+----------
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  b       | bbb
+  b       | bbbb
+  b       | bbbbb
+  b       | bbbbbb
+  b       | bbbbbbb
+  b       | bbbbbbbb
+ (12 rows)
+ 
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+  relname |    aa    | bb 
+ ---------+----------+----
+  b       | bbb      | 
+  b       | bbbb     | 
+  b       | bbbbb    | 
+  b       | bbbbbb   | 
+  b       | bbbbbbb  | 
+  b       | bbbbbbbb | 
+ (6 rows)
+ 
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+  relname |   aa   
+ ---------+--------
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+ (6 rows)
+ 
+ UPDATE b SET aa='new';
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+  relname |   aa   
+ ---------+--------
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  b       | new
+  b       | new
+  b       | new
+  b       | new
+  b       | new
+  b       | new
+ (12 rows)
+ 
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+  relname | aa  | bb 
+ ---------+-----+----
+  b       | new | 
+  b       | new | 
+  b       | new | 
+  b       | new | 
+  b       | new | 
+  b       | new | 
+ (6 rows)
+ 
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+  relname |   aa   
+ ---------+--------
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+  a       | zzzzzz
+ (6 rows)
+ 
+ UPDATE a SET aa='new';
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+  relname | aa  
+ ---------+-----
+  a       | new
+  a       | new
+  a       | new
+  a       | new
+  a       | new
+  a       | new
+  b       | new
+  b       | new
+  b       | new
+  b       | new
+  b       | new
+  b       | new
+ (12 rows)
+ 
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+  relname | aa  | bb 
+ ---------+-----+----
+  b       | new | 
+  b       | new | 
+  b       | new | 
+  b       | new | 
+  b       | new | 
+  b       | new | 
+ (6 rows)
+ 
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+  relname | aa  
+ ---------+-----
+  a       | new
+  a       | new
+  a       | new
+  a       | new
+  a       | new
+  a       | new
+ (6 rows)
+ 
+ DELETE FROM a;
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+  relname | aa 
+ ---------+----
+ (0 rows)
+ 
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+  relname | aa | bb 
+ ---------+----+----
+ (0 rows)
+ 
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+  relname | aa 
+ ---------+----
+ (0 rows)
+ 
+ DROP TABLE a CASCADE;
+ NOTICE:  drop cascades to foreign table b
+ DROP TABLE loct;
+ -- Check SELECT FOR UPDATE/SHARE with an inherited source table
+ create table loct1 (f1 int, f2 int, f3 int);
+ create table loct2 (f1 int, f2 int, f3 int);
+ create table foo (f1 int, f2 int);
+ create foreign table foo2 (f3 int) inherits (foo)
+   server loopback options (table_name 'loct1');
+ create table bar (f1 int, f2 int);
+ create foreign table bar2 (f3 int) inherits (bar)
+   server loopback options (table_name 'loct2');
+ insert into foo values(1,1);
+ insert into foo values(3,3);
+ insert into foo2 values(2,2,2);
+ insert into foo2 values(3,3,3);
+ insert into bar values(1,1);
+ insert into bar values(2,2);
+ insert into bar values(3,3);
+ insert into bar values(4,4);
+ insert into bar2 values(1,1,1);
+ insert into bar2 values(2,2,2);
+ insert into bar2 values(3,3,3);
+ insert into bar2 values(4,4,4);
+ explain (verbose, costs off)
+ select * from bar where f1 in (select f1 from foo) for update;
+                                           QUERY PLAN                                          
+ ----------------------------------------------------------------------------------------------
+  LockRows
+    Output: bar.f1, bar.f2, bar.ctid, bar.tableoid, bar.*, foo.ctid, foo.tableoid, foo.*
+    ->  Hash Join
+          Output: bar.f1, bar.f2, bar.ctid, bar.tableoid, bar.*, foo.ctid, foo.tableoid, foo.*
+          Hash Cond: (bar.f1 = foo.f1)
+          ->  Append
+                ->  Seq Scan on public.bar
+                      Output: bar.f1, bar.f2, bar.ctid, bar.tableoid, bar.*
+                ->  Foreign Scan on public.bar2
+                      Output: bar2.f1, bar2.f2, bar2.ctid, bar2.tableoid, bar2.*
+                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
+          ->  Hash
+                Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                ->  HashAggregate
+                      Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                      Group Key: foo.f1
+                      ->  Append
+                            ->  Seq Scan on public.foo
+                                  Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                            ->  Foreign Scan on public.foo2
+                                  Output: foo2.ctid, foo2.tableoid, foo2.*, foo2.f1
+                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
+ (22 rows)
+ 
+ select * from bar where f1 in (select f1 from foo) for update;
+  f1 | f2 
+ ----+----
+   1 |  1
+   2 |  2
+   3 |  3
+   1 |  1
+   2 |  2
+   3 |  3
+ (6 rows)
+ 
+ explain (verbose, costs off)
+ select * from bar where f1 in (select f1 from foo) for share;
+                                           QUERY PLAN                                          
+ ----------------------------------------------------------------------------------------------
+  LockRows
+    Output: bar.f1, bar.f2, bar.ctid, bar.tableoid, bar.*, foo.ctid, foo.tableoid, foo.*
+    ->  Hash Join
+          Output: bar.f1, bar.f2, bar.ctid, bar.tableoid, bar.*, foo.ctid, foo.tableoid, foo.*
+          Hash Cond: (bar.f1 = foo.f1)
+          ->  Append
+                ->  Seq Scan on public.bar
+                      Output: bar.f1, bar.f2, bar.ctid, bar.tableoid, bar.*
+                ->  Foreign Scan on public.bar2
+                      Output: bar2.f1, bar2.f2, bar2.ctid, bar2.tableoid, bar2.*
+                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR SHARE
+          ->  Hash
+                Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                ->  HashAggregate
+                      Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                      Group Key: foo.f1
+                      ->  Append
+                            ->  Seq Scan on public.foo
+                                  Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                            ->  Foreign Scan on public.foo2
+                                  Output: foo2.ctid, foo2.tableoid, foo2.*, foo2.f1
+                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
+ (22 rows)
+ 
+ select * from bar where f1 in (select f1 from foo) for share;
+  f1 | f2 
+ ----+----
+   1 |  1
+   2 |  2
+   3 |  3
+   1 |  1
+   2 |  2
+   3 |  3
+ (6 rows)
+ 
+ -- Check UPDATE with inherited target and an inherited source table
+ explain (verbose, costs off)
+ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
+                                          QUERY PLAN                                          
+ ---------------------------------------------------------------------------------------------
+  Update on public.bar
+    ->  Hash Join
+          Output: bar.f1, (bar.f2 + 100), bar.ctid, foo.ctid, foo.tableoid, foo.*
+          Hash Cond: (bar.f1 = foo.f1)
+          ->  Seq Scan on public.bar
+                Output: bar.f1, bar.f2, bar.ctid
+          ->  Hash
+                Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                ->  HashAggregate
+                      Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                      Group Key: foo.f1
+                      ->  Append
+                            ->  Seq Scan on public.foo
+                                  Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                            ->  Foreign Scan on public.foo2
+                                  Output: foo2.ctid, foo2.tableoid, foo2.*, foo2.f1
+                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
+    ->  Hash Join
+          Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, foo.ctid, foo.tableoid, foo.*
+          Hash Cond: (bar2.f1 = foo.f1)
+          ->  Foreign Scan on public.bar2
+                Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid
+                Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
+          ->  Hash
+                Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                ->  HashAggregate
+                      Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                      Group Key: foo.f1
+                      ->  Append
+                            ->  Seq Scan on public.foo
+                                  Output: foo.ctid, foo.tableoid, foo.*, foo.f1
+                            ->  Foreign Scan on public.foo2
+                                  Output: foo2.ctid, foo2.tableoid, foo2.*, foo2.f1
+                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
+ (34 rows)
+ 
+ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
+ select tableoid::regclass::text as relname, bar.* from bar order by 1,2;
+  relname | f1 | f2  
+ ---------+----+-----
+  bar     |  1 | 101
+  bar     |  2 | 102
+  bar     |  3 | 103
+  bar     |  4 |   4
+  bar2    |  1 | 101
+  bar2    |  2 | 102
+  bar2    |  3 | 103
+  bar2    |  4 |   4
+ (8 rows)
+ 
+ -- Check UPDATE with inherited target and an appendrel subquery
+ explain (verbose, costs off)
+ update bar set f2 = f2 + 100
+ from
+   ( select f1 from foo union all select f1+3 from foo ) ss
+ where bar.f1 = ss.f1;
+                                       QUERY PLAN                                      
+ --------------------------------------------------------------------------------------
+  Update on public.bar
+    ->  Hash Join
+          Output: bar.f1, (bar.f2 + 100), bar.ctid, (ROW(foo.f1))
+          Hash Cond: (foo.f1 = bar.f1)
+          ->  Append
+                ->  Seq Scan on public.foo
+                      Output: ROW(foo.f1), foo.f1
+                ->  Foreign Scan on public.foo2
+                      Output: ROW(foo2.f1), foo2.f1
+                      Remote SQL: SELECT f1 FROM public.loct1
+                ->  Seq Scan on public.foo foo_1
+                      Output: ROW((foo_1.f1 + 3)), (foo_1.f1 + 3)
+                ->  Foreign Scan on public.foo2 foo2_1
+                      Output: ROW((foo2_1.f1 + 3)), (foo2_1.f1 + 3)
+                      Remote SQL: SELECT f1 FROM public.loct1
+          ->  Hash
+                Output: bar.f1, bar.f2, bar.ctid
+                ->  Seq Scan on public.bar
+                      Output: bar.f1, bar.f2, bar.ctid
+    ->  Merge Join
+          Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, (ROW(foo.f1))
+          Merge Cond: (bar2.f1 = foo.f1)
+          ->  Sort
+                Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid
+                Sort Key: bar2.f1
+                ->  Foreign Scan on public.bar2
+                      Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid
+                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
+          ->  Sort
+                Output: (ROW(foo.f1)), foo.f1
+                Sort Key: foo.f1
+                ->  Append
+                      ->  Seq Scan on public.foo
+                            Output: ROW(foo.f1), foo.f1
+                      ->  Foreign Scan on public.foo2
+                            Output: ROW(foo2.f1), foo2.f1
+                            Remote SQL: SELECT f1 FROM public.loct1
+                      ->  Seq Scan on public.foo foo_1
+                            Output: ROW((foo_1.f1 + 3)), (foo_1.f1 + 3)
+                      ->  Foreign Scan on public.foo2 foo2_1
+                            Output: ROW((foo2_1.f1 + 3)), (foo2_1.f1 + 3)
+                            Remote SQL: SELECT f1 FROM public.loct1
+ (42 rows)
+ 
+ update bar set f2 = f2 + 100
+ from
+   ( select f1 from foo union all select f1+3 from foo ) ss
+ where bar.f1 = ss.f1;
+ select tableoid::regclass::text as relname, bar.* from bar order by 1,2;
+  relname | f1 | f2  
+ ---------+----+-----
+  bar     |  1 | 201
+  bar     |  2 | 202
+  bar     |  3 | 203
+  bar     |  4 | 104
+  bar2    |  1 | 201
+  bar2    |  2 | 202
+  bar2    |  3 | 203
+  bar2    |  4 | 104
+ (8 rows)
+ 
+ drop table foo cascade;
+ NOTICE:  drop cascades to foreign table foo2
+ drop table bar cascade;
+ NOTICE:  drop cascades to foreign table bar2
+ drop table loct1;
+ drop table loct2;
+ -- Test WHERE CURRENT OF
+ create table ltbl (a int, b text);
+ create table ptbl (a int, b text);
+ create table locc () inherits (ptbl);
+ insert into locc values(1, 'foo');
+ select * from ptbl;
+  a |  b  
+ ---+-----
+  1 | foo
+ (1 row)
+ 
+ create foreign table remc () inherits (ptbl)
+   server loopback options (table_name 'ltbl');
+ insert into remc values(2, 'bar');
+ begin;
+ declare c cursor for select 1 from ptbl where b = 'foo';
+ fetch from c;
+  ?column? 
+ ----------
+         1
+ (1 row)
+ 
+ update ptbl set b = null where current of c;
+ ERROR:  WHERE CURRENT OF is not supported for this table type
+ rollback;
+ select * from ptbl;
+  a |  b  
+ ---+-----
+  1 | foo
+  2 | bar
+ (2 rows)
+ 
+ drop table ptbl cascade;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to table locc
+ drop cascades to foreign table remc
+ drop table ltbl;
+ -- Test path reparameterization
+ create table loctbl (id int, x int);
+ create table parent (id int, x int);
+ create table locchild () inherits (parent);
+ create foreign table remchild () inherits (parent)
+   server loopback options (table_name 'loctbl');
+ insert into parent select x, x from generate_series(0,1000) x;
+ insert into locchild select x, x from generate_series(0,1000) x;
+ insert into remchild select x, x from generate_series(0,1000) x;
+ create table inttbl (f1) as select x from generate_series(0,1000) x;
+ analyze inttbl;
+ analyze parent;
+ analyze locchild;
+ analyze remchild;
+ explain (costs off)
+ select * from parent join (select f1 from inttbl limit 1) ss on id = f1;
+               QUERY PLAN              
+ --------------------------------------
+  Hash Join
+    Hash Cond: (parent.id = inttbl.f1)
+    ->  Append
+          ->  Seq Scan on parent
+          ->  Seq Scan on locchild
+          ->  Foreign Scan on remchild
+    ->  Hash
+          ->  Limit
+                ->  Seq Scan on inttbl
+ (9 rows)
+ 
+ select * from parent join (select f1 from inttbl limit 1) ss on id = f1;
+  id | x | f1 
+ ----+---+----
+   0 | 0 |  0
+   0 | 0 |  0
+   0 | 0 |  0
+ (3 rows)
+ 
+ create index parent_idx on parent (id);
+ create index locchild_idx on locchild (id);
+ analyze parent;
+ analyze locchild;
+ explain (costs off)
+ select * from parent join (select f1 from inttbl limit 1) ss on id = f1;
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Nested Loop
+    ->  Limit
+          ->  Seq Scan on inttbl
+    ->  Append
+          ->  Index Scan using parent_idx on parent
+                Index Cond: (id = inttbl.f1)
+          ->  Index Scan using locchild_idx on locchild
+                Index Cond: (id = inttbl.f1)
+          ->  Foreign Scan on remchild
+ (9 rows)
+ 
+ select * from parent join (select f1 from inttbl limit 1) ss on id = f1;
+  id | x | f1 
+ ----+---+----
+   0 | 0 |  0
+   0 | 0 |  0
+   0 | 0 |  0
+ (3 rows)
+ 
+ drop table parent cascade;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to table locchild
+ drop cascades to foreign table remchild
+ drop table loctbl;
+ drop table inttbl;
+ -- ===================================================================
  -- test IMPORT FOREIGN SCHEMA
  -- ===================================================================
  CREATE SCHEMA import_source;
*** a/contrib/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
***************
*** 238,243 **** static void postgresGetForeignRelSize(PlannerInfo *root,
--- 238,248 ----
  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,
***************
*** 341,346 **** postgres_fdw_handler(PG_FUNCTION_ARGS)
--- 346,352 ----
  	/* Functions for scanning foreign tables */
  	routine->GetForeignRelSize = postgresGetForeignRelSize;
  	routine->GetForeignPaths = postgresGetForeignPaths;
+ 	routine->ReparameterizeForeignPath = postgresReparameterizeForeignPath;
  	routine->GetForeignPlan = postgresGetForeignPlan;
  	routine->BeginForeignScan = postgresBeginForeignScan;
  	routine->IterateForeignScan = postgresIterateForeignScan;
***************
*** 731,736 **** postgresGetForeignPaths(PlannerInfo *root,
--- 737,784 ----
  }
  
  /*
+  * 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;
+ 
+ 	/* Recreate 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
   */
***************
*** 823,828 **** postgresGetForeignPlan(PlannerInfo *root,
--- 871,887 ----
  	{
  		RowMarkClause *rc = get_parse_rowmark(root->parse, baserel->relid);
  
+ 		if (rc == NULL)
+ 		{
+ 			PlanRowMark *prm = get_plan_rowmark(root->rowMarks, baserel->relid);
+ 
+ 			if (prm)
+ 			{
+ 				if (prm->rti != prm->prti)
+ 					rc = get_parse_rowmark(root->parse, prm->prti);
+ 			}
+ 		}
+ 
  		if (rc)
  		{
  			/*
***************
*** 1777,1787 **** 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);
  
  		/* Use rows/width estimates made by set_baserel_size_estimates. */
  		rows = baserel->rows;
--- 1836,1843 ----
  	}
  	else
  	{
! 		Selectivity join_sel;
! 		QualCost	join_cost;
  
  		/* Use rows/width estimates made by set_baserel_size_estimates. */
  		rows = baserel->rows;
***************
*** 1794,1810 **** estimate_path_cost_size(PlannerInfo *root,
  		retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
  		retrieved_rows = Min(retrieved_rows, baserel->tuples);
  
  		/*
  		 * Cost as though this were a seqscan, which is pessimistic.  We
! 		 * effectively imagine the local_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;
  		run_cost += cpu_per_tuple * baserel->tuples;
  
  		total_cost = startup_cost + run_cost;
--- 1850,1878 ----
  		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 and join_conds are being
! 		 * evaluated remotely, too.
  		 */
  		startup_cost = 0;
  		run_cost = 0;
  		run_cost += seq_page_cost * baserel->pages;
  
! 		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;
*** a/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/contrib/postgres_fdw/sql/postgres_fdw.sql
***************
*** 611,616 **** UPDATE rem1 SET f2 = 'testo';
--- 611,786 ----
  INSERT INTO rem1(f2) VALUES ('test') RETURNING ctid;
  
  -- ===================================================================
+ -- test inheritance features
+ -- ===================================================================
+ 
+ CREATE TABLE a (aa TEXT);
+ CREATE TABLE loct (aa TEXT, bb TEXT);
+ CREATE FOREIGN TABLE b (bb TEXT) INHERITS (a)
+   SERVER loopback OPTIONS (table_name 'loct');
+ 
+ INSERT INTO a(aa) VALUES('aaa');
+ INSERT INTO a(aa) VALUES('aaaa');
+ INSERT INTO a(aa) VALUES('aaaaa');
+ INSERT INTO a(aa) VALUES('aaaaaa');
+ INSERT INTO a(aa) VALUES('aaaaaaa');
+ INSERT INTO a(aa) VALUES('aaaaaaaa');
+ 
+ INSERT INTO b(aa) VALUES('bbb');
+ INSERT INTO b(aa) VALUES('bbbb');
+ INSERT INTO b(aa) VALUES('bbbbb');
+ INSERT INTO b(aa) VALUES('bbbbbb');
+ INSERT INTO b(aa) VALUES('bbbbbbb');
+ INSERT INTO b(aa) VALUES('bbbbbbbb');
+ 
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+ 
+ UPDATE a SET aa='zzzzzz' WHERE aa LIKE 'aaa%';
+ 
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+ 
+ UPDATE b SET aa='new';
+ 
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+ 
+ UPDATE a SET aa='new';
+ 
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+ 
+ DELETE FROM a;
+ 
+ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid;
+ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid;
+ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid;
+ 
+ DROP TABLE a CASCADE;
+ DROP TABLE loct;
+ 
+ -- Check SELECT FOR UPDATE/SHARE with an inherited source table
+ create table loct1 (f1 int, f2 int, f3 int);
+ create table loct2 (f1 int, f2 int, f3 int);
+ 
+ create table foo (f1 int, f2 int);
+ create foreign table foo2 (f3 int) inherits (foo)
+   server loopback options (table_name 'loct1');
+ create table bar (f1 int, f2 int);
+ create foreign table bar2 (f3 int) inherits (bar)
+   server loopback options (table_name 'loct2');
+ 
+ insert into foo values(1,1);
+ insert into foo values(3,3);
+ insert into foo2 values(2,2,2);
+ insert into foo2 values(3,3,3);
+ insert into bar values(1,1);
+ insert into bar values(2,2);
+ insert into bar values(3,3);
+ insert into bar values(4,4);
+ insert into bar2 values(1,1,1);
+ insert into bar2 values(2,2,2);
+ insert into bar2 values(3,3,3);
+ insert into bar2 values(4,4,4);
+ 
+ explain (verbose, costs off)
+ select * from bar where f1 in (select f1 from foo) for update;
+ select * from bar where f1 in (select f1 from foo) for update;
+ 
+ explain (verbose, costs off)
+ select * from bar where f1 in (select f1 from foo) for share;
+ select * from bar where f1 in (select f1 from foo) for share;
+ 
+ -- Check UPDATE with inherited target and an inherited source table
+ explain (verbose, costs off)
+ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
+ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
+ 
+ select tableoid::regclass::text as relname, bar.* from bar order by 1,2;
+ 
+ -- Check UPDATE with inherited target and an appendrel subquery
+ explain (verbose, costs off)
+ update bar set f2 = f2 + 100
+ from
+   ( select f1 from foo union all select f1+3 from foo ) ss
+ where bar.f1 = ss.f1;
+ update bar set f2 = f2 + 100
+ from
+   ( select f1 from foo union all select f1+3 from foo ) ss
+ where bar.f1 = ss.f1;
+ 
+ select tableoid::regclass::text as relname, bar.* from bar order by 1,2;
+ 
+ drop table foo cascade;
+ drop table bar cascade;
+ drop table loct1;
+ drop table loct2;
+ 
+ -- Test WHERE CURRENT OF
+ create table ltbl (a int, b text);
+ 
+ create table ptbl (a int, b text);
+ create table locc () inherits (ptbl);
+ insert into locc values(1, 'foo');
+ 
+ select * from ptbl;
+ 
+ create foreign table remc () inherits (ptbl)
+   server loopback options (table_name 'ltbl');
+ insert into remc values(2, 'bar');
+ 
+ begin;
+ declare c cursor for select 1 from ptbl where b = 'foo';
+ fetch from c;
+ update ptbl set b = null where current of c;
+ rollback;
+ 
+ select * from ptbl;
+ 
+ drop table ptbl cascade;
+ drop table ltbl;
+ 
+ -- Test path reparameterization
+ create table loctbl (id int, x int);
+ 
+ create table parent (id int, x int);
+ create table locchild () inherits (parent);
+ create foreign table remchild () inherits (parent)
+   server loopback options (table_name 'loctbl');
+ insert into parent select x, x from generate_series(0,1000) x;
+ insert into locchild select x, x from generate_series(0,1000) x;
+ insert into remchild select x, x from generate_series(0,1000) x;
+ 
+ create table inttbl (f1) as select x from generate_series(0,1000) x;
+ 
+ analyze inttbl;
+ analyze parent;
+ analyze locchild;
+ analyze remchild;
+ 
+ explain (costs off)
+ select * from parent join (select f1 from inttbl limit 1) ss on id = f1;
+ select * from parent join (select f1 from inttbl limit 1) ss on id = f1;
+ 
+ create index parent_idx on parent (id);
+ create index locchild_idx on locchild (id);
+ analyze parent;
+ analyze locchild;
+ 
+ explain (costs off)
+ select * from parent join (select f1 from inttbl limit 1) ss on id = f1;
+ select * from parent join (select f1 from inttbl limit 1) ss on id = f1;
+ 
+ drop table parent cascade;
+ drop table loctbl;
+ drop table inttbl;
+ 
+ -- ===================================================================
  -- test IMPORT FOREIGN SCHEMA
  -- ===================================================================
  
*** a/doc/src/sgml/ddl.sgml
--- b/doc/src/sgml/ddl.sgml
***************
*** 2224,2229 **** VALUES ('Albany', NULL, NULL, 'NY');
--- 2224,2245 ----
     further privileges to be granted.
    </para>
  
+   <para>
+    Note that a foreign table can also inherit from more than one parent
+    table (see <xref linkend="ddl-foreign-data">).
+    Like normal tables, table inheritance is typically established when
+    the foreign table is created, using the <literal>INHERITS</> clause of
+    the <xref linkend="sql-createforeigntable"> statement.  Alternatively,
+    a foreign table which is already defined in a compatible way can have
+    a new parent relationship added, using the <literal>INHERIT</literal>
+    variant of <xref linkend="sql-alterforeigntable">.
+    <command>CREATE FOREIGN TABLE</command> and
+    <command>ALTER FOREIGN TABLE</command> follow the same rules for
+    duplicate column merging and rejection that apply during
+    <command>CREATE TABLE</command> and <command>ALTER TABLE</command>,
+    respectively.
+   </para>
+ 
   <sect2 id="ddl-inherit-caveats">
    <title>Caveats</title>
  
***************
*** 2371,2377 **** VALUES ('Albany', NULL, NULL, 'NY');
      table of a single parent table.  The parent table itself is normally
      empty; it exists just to represent the entire data set.  You should be
      familiar with inheritance (see <xref linkend="ddl-inherit">) before
!     attempting to set up partitioning.
     </para>
  
     <para>
--- 2387,2396 ----
      table of a single parent table.  The parent table itself is normally
      empty; it exists just to represent the entire data set.  You should be
      familiar with inheritance (see <xref linkend="ddl-inherit">) before
!     attempting to set up partitioning.  (The setup and management of
!     partitioned tables illustrated in this section assume that each
!     partition is a normal table.  However, you can do that in a similar way
!     for cases where some or all partitions are foreign tables.)
     </para>
  
     <para>
***************
*** 2434,2441 **** VALUES ('Albany', NULL, NULL, 'NY');
         </para>
  
         <para>
!         We will refer to the child tables as partitions, though they
!         are in every way normal <productname>PostgreSQL</> tables.
         </para>
        </listitem>
  
--- 2453,2460 ----
         </para>
  
         <para>
!         We will refer to the child tables as partitions, though we assume
!         that they are normal <productname>PostgreSQL</> tables.
         </para>
        </listitem>
  
*** a/doc/src/sgml/fdwhandler.sgml
--- b/doc/src/sgml/fdwhandler.sgml
***************
*** 162,167 **** GetForeignPaths (PlannerInfo *root,
--- 162,198 ----
  
      <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,
***************
*** 868,877 **** 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.
      </para>
  
      <para>
--- 899,908 ----
  
      <para>
       The FDW callback functions <function>GetForeignRelSize</>,
!      <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>
***************
*** 901,914 **** 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.
      </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.
       <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
--- 932,948 ----
       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>ReparameterizeForeignPath</>
!      and/or
!      <function>GetForeignPaths</> or <function>ReparameterizeForeignPath</>
!      to <function>GetForeignPlan</>, thereby avoiding recalculation.
      </para>
  
      <para>
!      <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
***************
*** 951,960 **** 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
       <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
--- 985,995 ----
       <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</> 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
*** a/doc/src/sgml/ref/alter_foreign_table.sgml
--- b/doc/src/sgml/ref/alter_foreign_table.sgml
***************
*** 42,51 **** ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab
--- 42,55 ----
      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 ]
      DISABLE TRIGGER [ <replaceable class="PARAMETER">trigger_name</replaceable> | ALL | USER ]
      ENABLE TRIGGER [ <replaceable class="PARAMETER">trigger_name</replaceable> | ALL | USER ]
      ENABLE REPLICA TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable>
      ENABLE ALWAYS TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable>
+     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>
***************
*** 164,169 **** ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab
--- 168,217 ----
     </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>
***************
*** 285,290 **** ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab
--- 333,356 ----
       </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>
***************
*** 336,341 **** ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab
--- 402,417 ----
       </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>
***************
*** 365,374 **** 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.
     </para>
  
     <para>
--- 441,450 ----
     <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 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>
*** a/doc/src/sgml/ref/alter_table.sgml
--- b/doc/src/sgml/ref/alter_table.sgml
***************
*** 967,972 **** ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
--- 967,979 ----
     </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,980 **** ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
--- 982,1000 ----
     </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>
  
*** a/doc/src/sgml/ref/analyze.sgml
--- b/doc/src/sgml/ref/analyze.sgml
***************
*** 200,205 **** ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> [
--- 200,212 ----
    </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.
*** a/doc/src/sgml/ref/create_foreign_table.sgml
--- b/doc/src/sgml/ref/create_foreign_table.sgml
***************
*** 19,27 ****
   <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> [ ... ] ]
      [, ... ]
  ] )
    SERVER <replaceable class="parameter">server_name</replaceable>
  [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
  
--- 19,29 ----
   <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>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,36 **** CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
--- 32,44 ----
  [ 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,143 **** CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
--- 146,172 ----
     </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,164 **** CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
--- 188,205 ----
     </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,192 **** CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
--- 228,251 ----
  
   </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>
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 2217,2222 **** AddRelationNewConstraints(Relation rel,
--- 2217,2228 ----
  		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);
*** a/src/backend/commands/analyze.c
--- b/src/backend/commands/analyze.c
***************
*** 82,87 **** int			default_statistics_target = 100;
--- 82,88 ----
  
  /* 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;
  
  
***************
*** 102,107 **** static int acquire_sample_rows(Relation onerel, int elevel,
--- 103,109 ----
  					HeapTuple *rows, int targrows,
  					double *totalrows, double *totaldeadrows);
  static int	compare_rows(const void *a, const void *b);
+ static bool has_foreign(Oid parentrelId, List *tableOIDs);
  static int acquire_inherited_sample_rows(Relation onerel, int elevel,
  							  HeapTuple *rows, int targrows,
  							  double *totalrows, double *totaldeadrows);
***************
*** 115,121 **** 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)
  {
  	Relation	onerel;
  	int			elevel;
--- 117,126 ----
   *	analyze_rel() -- analyze one relation
   */
  void
! analyze_rel(Oid relid,
! 			VacuumStmt *vacstmt,
! 			VacuumMode vacmode,
! 			BufferAccessStrategy bstrategy)
  {
  	Relation	onerel;
  	int			elevel;
***************
*** 129,134 **** analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
--- 134,140 ----
  		elevel = DEBUG2;
  
  	/* Set up static variables */
+ 	vac_mode = vacmode;
  	vac_strategy = bstrategy;
  
  	/*
***************
*** 1439,1444 **** compare_rows(const void *a, const void *b)
--- 1445,1484 ----
  
  
  /*
+  * Check wether to contain at least one foreign table
+  */
+ static bool
+ has_foreign(Oid parentrelId, List *tableOIDs)
+ {
+ 	bool		result = false;
+ 	ListCell   *lc;
+ 
+ 	/* There are no children */
+ 	if (list_length(tableOIDs) < 2)
+ 		return result;
+ 
+ 	foreach(lc, tableOIDs)
+ 	{
+ 		Oid			childOID = lfirst_oid(lc);
+ 		Relation	childrel;
+ 
+ 		/* Parent should not be foreign */
+ 		if (childOID == parentrelId)
+ 			continue;
+ 
+ 		/* We already got the needed lock */
+ 		childrel = heap_open(childOID, NoLock);
+ 		if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ 		{
+ 			/* Found it */
+ 			result = true;
+ 		}
+ 		heap_close(childrel, NoLock);
+ 	}
+ 	return result;
+ }
+ 
+ /*
   * acquire_inherited_sample_rows -- acquire sample rows from inheritance tree
   *
   * This has the same API as acquire_sample_rows, except that rows are
***************
*** 1452,1457 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
--- 1492,1498 ----
  {
  	List	   *tableOIDs;
  	Relation   *rels;
+ 	AcquireSampleRowsFunc *acquirefunc;
  	double	   *relblocks;
  	double		totalblocks;
  	int			numrows,
***************
*** 1482,1491 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
--- 1523,1544 ----
  	}
  
  	/*
+ 	 * If we are not in VAC_MODE_SINGLE case and the inheritance set contains
+ 	 * at least one foreign table, then fail.
+ 	 */
+ 	if (vac_mode != VAC_MODE_SINGLE)
+ 	{
+ 		if (has_foreign(RelationGetRelid(onerel), tableOIDs))
+ 			return 0;
+ 	}
+ 
+ 	/*
  	 * Count the blocks in all the relations.  The result could overflow
  	 * 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,1513 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
  		}
  
  		rels[nrels] = childrel;
! 		relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel);
  		totalblocks += relblocks[nrels];
  		nrels++;
  	}
--- 1560,1595 ----
  		}
  
  		rels[nrels] = childrel;
! 
! 		if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
! 		{
! 			FdwRoutine *fdwroutine;
! 			BlockNumber relpages = 0;
! 			bool		ok = false;
! 
! 			/* 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,1530 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
--- 1607,1613 ----
  	{
  		Relation	childrel = rels[i];
  		double		childblocks = relblocks[i];
+ 		AcquireSampleRowsFunc childacquirefunc = acquirefunc[i];
  
  		if (childblocks > 0)
  		{
***************
*** 1540,1551 **** 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);
  
  				/* We may need to convert from child's rowtype to parent's */
  				if (childrows > 0 &&
--- 1623,1634 ----
  							tdrows;
  
  				/* Fetch a random sample of the child's rows */
! 				childrows = childacquirefunc(childrel,
! 											 elevel,
! 											 rows + numrows,
! 											 childtargrows,
! 											 &trows,
! 											 &tdrows);
  
  				/* We may need to convert from child's rowtype to parent's */
  				if (childrows > 0 &&
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 309,315 **** 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);
  static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
  					  LOCKMODE lockmode);
  static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
--- 309,316 ----
  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,
! 				  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,
***************
*** 465,474 **** 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,
--- 466,471 ----
***************
*** 558,563 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 555,584 ----
  							 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
***************
*** 3066,3089 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
  			 * rules.
  			 */
  			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
  			/* No command-specific prep needed */
  			pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
  			break;
  		case AT_DropNotNull:	/* ALTER COLUMN DROP NOT NULL */
  			ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
  			/* No command-specific prep needed */
  			pass = AT_PASS_DROP;
  			break;
  		case AT_SetNotNull:		/* ALTER COLUMN SET NOT NULL */
  			ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
! 			ATSimpleRecursion(wqueue, rel, cmd, recurse, 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);
  			/* Performs own permission checks */
  			ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode);
  			pass = AT_PASS_MISC;
--- 3087,3114 ----
  			 * rules.
  			 */
  			ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
! 			/* 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);
! 			/* 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);
! 			/* 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 */
! 			/* 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;
***************
*** 3096,3102 **** 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);
  			/* No command-specific prep needed */
  			pass = AT_PASS_MISC;
  			break;
--- 3121,3128 ----
  			break;
  		case AT_SetStorage:		/* ALTER COLUMN SET STORAGE */
  			ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
! 			/* 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;
***************
*** 3114,3120 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
  			pass = AT_PASS_ADD_INDEX;
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
! 			ATSimplePermissions(rel, ATT_TABLE);
  			/* Recursion occurs during execution phase */
  			/* No command-specific prep needed except saving recurse flag */
  			if (recurse)
--- 3140,3146 ----
  			pass = AT_PASS_ADD_INDEX;
  			break;
  		case AT_AddConstraint:	/* ADD CONSTRAINT */
! 			ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
  			/* Recursion occurs during execution phase */
  			/* No command-specific prep needed except saving recurse flag */
  			if (recurse)
***************
*** 3128,3134 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATSimplePermissions(rel, ATT_TABLE);
  			/* Recursion occurs during execution phase */
  			/* No command-specific prep needed except saving recurse flag */
  			if (recurse)
--- 3154,3160 ----
  			pass = AT_PASS_ADD_CONSTR;
  			break;
  		case AT_DropConstraint:	/* DROP CONSTRAINT */
! 			ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
  			/* Recursion occurs during execution phase */
  			/* No command-specific prep needed except saving recurse flag */
  			if (recurse)
***************
*** 3196,3206 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
  			pass = AT_PASS_MISC;
  			break;
  		case AT_AddInherit:		/* INHERIT */
! 			ATSimplePermissions(rel, ATT_TABLE);
  			/* This command never recurses */
  			ATPrepAddInherit(rel);
  			pass = AT_PASS_MISC;
  			break;
  		case AT_AlterConstraint:		/* ALTER CONSTRAINT */
  			ATSimplePermissions(rel, ATT_TABLE);
  			pass = AT_PASS_MISC;
--- 3222,3238 ----
  			pass = AT_PASS_MISC;
  			break;
  		case AT_AddInherit:		/* INHERIT */
! 			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;
***************
*** 3234,3240 **** 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);
--- 3266,3271 ----
***************
*** 4175,4181 **** ATWrongRelkindError(Relation rel, int allowed_targets)
   */
  static void
  ATSimpleRecursion(List **wqueue, Relation rel,
! 				  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
  {
  	/*
  	 * Propagate to children if desired.  Non-table relations never have
--- 4206,4213 ----
   */
  static void
  ATSimpleRecursion(List **wqueue, Relation rel,
! 				  AlterTableCmd *cmd, bool recurse,
! 				  bool include_foreign, LOCKMODE lockmode)
  {
  	/*
  	 * Propagate to children if desired.  Non-table relations never have
***************
*** 4203,4210 **** 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);
  			relation_close(childrel, NoLock);
  		}
  	}
--- 4235,4246 ----
  				continue;
  			/* find_all_inheritors already got lock */
  			childrel = relation_open(childrelid, NoLock);
! 			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);
  		}
  	}
***************
*** 4494,4500 **** 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);
  
  	attrdesc = heap_open(AttributeRelationId, RowExclusiveLock);
  
--- 4530,4536 ----
  
  	/* At top level, permission check was done in ATPrepCmd, else do it */
  	if (recursing)
! 		ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
  
  	attrdesc = heap_open(AttributeRelationId, RowExclusiveLock);
  
***************
*** 4790,4795 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
--- 4826,4836 ----
  
  		/* 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 */
***************
*** 5390,5396 **** 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);
  
  	/*
  	 * get the number of the attribute
--- 5431,5437 ----
  
  	/* At top level, permission check was done in ATPrepCmd, else do it */
  	if (recursing)
! 		ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
  
  	/*
  	 * get the number of the attribute
***************
*** 5782,5788 **** 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);
  
  	/*
  	 * Call AddRelationNewConstraints to do the work, making sure it works on
--- 5823,5836 ----
  
  	/* At top level, permission check was done in ATPrepCmd, else do it */
  	if (recursing)
! 		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
***************
*** 5793,5801 **** 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.
  	 */
  	newcons = AddRelationNewConstraints(rel, NIL,
! 										list_make1(copyObject(constr)),
  										recursing,		/* allow_merge */
  										!recursing,		/* is_local */
  										is_readd);		/* is_internal */
--- 5841,5857 ----
  	 * 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(constr),
  										recursing,		/* allow_merge */
  										!recursing,		/* is_local */
  										is_readd);		/* is_internal */
***************
*** 7282,7288 **** ATExecDropConstraint(Relation rel, const char *constrName,
  
  	/* At top level, permission check was done in ATPrepCmd, else do it */
  	if (recursing)
! 		ATSimplePermissions(rel, ATT_TABLE);
  
  	conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
  
--- 7338,7344 ----
  
  	/* At top level, permission check was done in ATPrepCmd, else do it */
  	if (recursing)
! 		ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
  
  	conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
  
***************
*** 7617,7623 **** ATPrepAlterColumnType(List **wqueue,
  	 * alter would put them out of step.
  	 */
  	if (recurse)
! 		ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
  	else if (!recursing &&
  			 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
  		ereport(ERROR,
--- 7673,7682 ----
  	 * alter would put them out of step.
  	 */
  	if (recurse)
! 	{
! 		/* 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,
*** a/src/backend/commands/vacuum.c
--- b/src/backend/commands/vacuum.c
***************
*** 61,66 **** int			vacuum_multixact_freeze_table_age;
--- 61,67 ----
  
  /* 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;
  
  
***************
*** 149,154 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
--- 150,169 ----
  										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.
  	 */
***************
*** 251,257 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
  					PushActiveSnapshot(GetTransactionSnapshot());
  				}
  
! 				analyze_rel(relid, vacstmt, vac_strategy);
  
  				if (use_own_xacts)
  				{
--- 266,272 ----
  					PushActiveSnapshot(GetTransactionSnapshot());
  				}
  
! 				analyze_rel(relid, vacstmt, vac_mode, vac_strategy);
  
  				if (use_own_xacts)
  				{
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 2275,2280 **** EvalPlanQualFetchRowMarks(EPQState *epqstate)
--- 2275,2300 ----
  
  			Assert(erm->markType == ROW_MARK_COPY);
  
+ 			/* if child rel, must check whether it produced this row */
+ 			if (erm->rti != erm->prti)
+ 			{
+ 				Oid			tableoid;
+ 
+ 				datum = ExecGetJunkAttribute(epqstate->origslot,
+ 											 aerm->toidAttNo,
+ 											 &isNull);
+ 				/* non-locked rels could be on the inside of outer joins */
+ 				if (isNull)
+ 					continue;
+ 				tableoid = DatumGetObjectId(datum);
+ 
+ 				if (tableoid != RelationGetRelid(erm->relation))
+ 				{
+ 					/* this child is inactive right now */
+ 					continue;
+ 				}
+ 			}
+ 
  			/* fetch the whole-row Var for the relation */
  			datum = ExecGetJunkAttribute(epqstate->origslot,
  										 aerm->wholeAttNo,
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1994,1999 **** _copyRangeTblEntry(const RangeTblEntry *from)
--- 1994,2000 ----
  	COPY_NODE_FIELD(eref);
  	COPY_SCALAR_FIELD(lateral);
  	COPY_SCALAR_FIELD(inh);
+ 	COPY_SCALAR_FIELD(hasForeign);
  	COPY_SCALAR_FIELD(inFromCl);
  	COPY_SCALAR_FIELD(requiredPerms);
  	COPY_SCALAR_FIELD(checkAsUser);
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 2315,2320 **** _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
--- 2315,2321 ----
  	COMPARE_NODE_FIELD(eref);
  	COMPARE_SCALAR_FIELD(lateral);
  	COMPARE_SCALAR_FIELD(inh);
+ 	COMPARE_SCALAR_FIELD(hasForeign);
  	COMPARE_SCALAR_FIELD(inFromCl);
  	COMPARE_SCALAR_FIELD(requiredPerms);
  	COMPARE_SCALAR_FIELD(checkAsUser);
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 2419,2424 **** _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
--- 2419,2425 ----
  
  	WRITE_BOOL_FIELD(lateral);
  	WRITE_BOOL_FIELD(inh);
+ 	WRITE_BOOL_FIELD(hasForeign);
  	WRITE_BOOL_FIELD(inFromCl);
  	WRITE_UINT_FIELD(requiredPerms);
  	WRITE_OID_FIELD(checkAsUser);
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 1248,1253 **** _readRangeTblEntry(void)
--- 1248,1254 ----
  
  	READ_BOOL_FIELD(lateral);
  	READ_BOOL_FIELD(inh);
+ 	READ_BOOL_FIELD(hasForeign);
  	READ_BOOL_FIELD(inFromCl);
  	READ_UINT_FIELD(requiredPerms);
  	READ_OID_FIELD(checkAsUser);
*** a/src/backend/optimizer/prep/preptlist.c
--- b/src/backend/optimizer/prep/preptlist.c
***************
*** 111,116 **** preprocess_targetlist(PlannerInfo *root, List *tlist)
--- 111,118 ----
  			/* if parent of inheritance tree, need the tableoid too */
  			if (rc->isParent)
  			{
+ 				RangeTblEntry *rte = rt_fetch(rc->rti, parse->rtable);
+ 
  				var = makeVar(rc->rti,
  							  TableOidAttributeNumber,
  							  OIDOID,
***************
*** 123,128 **** preprocess_targetlist(PlannerInfo *root, List *tlist)
--- 125,146 ----
  									  pstrdup(resname),
  									  true);
  				tlist = lappend(tlist, tle);
+ 
+ 				/* if including foreign tables, fetch the whole row too */
+ 				if (rte->hasForeign)
+ 				{
+ 					/* Not a table, so we need the whole row as a junk var */
+ 					var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
+ 										  rc->rti,
+ 										  0,
+ 										  false);
+ 					snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
+ 					tle = makeTargetEntry((Expr *) var,
+ 										  list_length(tlist) + 1,
+ 										  pstrdup(resname),
+ 										  true);
+ 					tlist = lappend(tlist, tle);
+ 				}
  			}
  		}
  		else
*** a/src/backend/optimizer/prep/prepunion.c
--- b/src/backend/optimizer/prep/prepunion.c
***************
*** 1243,1248 **** expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
--- 1243,1249 ----
  	LOCKMODE	lockmode;
  	List	   *inhOIDs;
  	List	   *appinfos;
+ 	bool		hasForeign;
  	ListCell   *l;
  
  	/* Does RT entry allow inheritance? */
***************
*** 1315,1320 **** expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
--- 1316,1322 ----
  
  	/* Scan the inheritance set and expand it */
  	appinfos = NIL;
+ 	hasForeign = false;
  	foreach(l, inhOIDs)
  	{
  		Oid			childOID = lfirst_oid(l);
***************
*** 1344,1354 **** 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.
  		 */
  		childrte = copyObject(rte);
  		childrte->relid = childOID;
  		childrte->inh = false;
  		childrte->requiredPerms = 0;
  		parse->rtable = lappend(parse->rtable, childrte);
--- 1346,1357 ----
  		/*
  		 * 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,
! 		 * 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);
***************
*** 1394,1400 **** expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
  			newrc->rti = childRTindex;
  			newrc->prti = rti;
  			newrc->rowmarkId = oldrc->rowmarkId;
! 			newrc->markType = oldrc->markType;
  			newrc->noWait = oldrc->noWait;
  			newrc->isParent = false;
  
--- 1397,1409 ----
  			newrc->rti = childRTindex;
  			newrc->prti = rti;
  			newrc->rowmarkId = oldrc->rowmarkId;
! 			if (childrte->relkind == RELKIND_FOREIGN_TABLE)
! 		    {
! 				hasForeign = true;
! 				newrc->markType = ROW_MARK_COPY;
! 			}
! 			else
! 				newrc->markType = oldrc->markType;
  			newrc->noWait = oldrc->noWait;
  			newrc->isParent = false;
  
***************
*** 1422,1427 **** expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
--- 1431,1440 ----
  
  	/* Otherwise, OK to add to root->append_rel_list */
  	root->append_rel_list = list_concat(root->append_rel_list, appinfos);
+ 
+ 	/* And mark the parent table as having children that are foreign, if so */
+ 	if (hasForeign)
+ 		rte->hasForeign = true;
  }
  
  /*
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
***************
*** 16,21 ****
--- 16,22 ----
  
  #include <math.h>
  
+ #include "foreign/fdwapi.h"
  #include "miscadmin.h"
  #include "nodes/nodeFuncs.h"
  #include "optimizer/clauses.h"
***************
*** 1921,1926 **** reparameterize_path(PlannerInfo *root, Path *path,
--- 1922,1952 ----
  		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;
  	}
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 4225,4256 **** AlterForeignServerStmt: ALTER SERVER name foreign_server_version alter_generic_o
  CreateForeignTableStmt:
  		CREATE FOREIGN TABLE qualified_name
  			'(' OptTableElementList ')'
! 			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.if_not_exists = false;
  					/* FDW-specific data */
! 					n->servername = $9;
! 					n->options = $10;
  					$$ = (Node *) n;
  				}
  		| CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name
  			'(' OptTableElementList ')'
! 			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.if_not_exists = true;
  					/* FDW-specific data */
! 					n->servername = $12;
! 					n->options = $13;
  					$$ = (Node *) n;
  				}
  		;
--- 4225,4256 ----
  CreateForeignTableStmt:
  		CREATE FOREIGN TABLE qualified_name
  			'(' OptTableElementList ')'
! 			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 = $8;
  					n->base.if_not_exists = false;
  					/* FDW-specific data */
! 					n->servername = $10;
! 					n->options = $11;
  					$$ = (Node *) n;
  				}
  		| CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name
  			'(' OptTableElementList ')'
! 			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 = $11;
  					n->base.if_not_exists = true;
  					/* FDW-specific data */
! 					n->servername = $13;
! 					n->options = $14;
  					$$ = (Node *) n;
  				}
  		;
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
***************
*** 515,526 **** 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;
  
--- 515,520 ----
***************
*** 529,535 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
  				if (cxt->isforeign)
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					errmsg("constraints are not supported on foreign tables"),
  							 parser_errposition(cxt->pstate,
  												constraint->location)));
  				if (constraint->keys == NIL)
--- 523,529 ----
  				if (cxt->isforeign)
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					errmsg("primary key or unique constraints are not supported on foreign tables"),
  							 parser_errposition(cxt->pstate,
  												constraint->location)));
  				if (constraint->keys == NIL)
***************
*** 546,552 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
  				if (cxt->isforeign)
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					errmsg("constraints are not supported on foreign tables"),
  							 parser_errposition(cxt->pstate,
  												constraint->location)));
  
--- 540,546 ----
  				if (cxt->isforeign)
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					errmsg("foreign key constraints are not supported on foreign tables"),
  							 parser_errposition(cxt->pstate,
  												constraint->location)));
  
***************
*** 605,614 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
  static void
  transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
  {
! 	if (cxt->isforeign)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("constraints are not supported on foreign tables"),
  				 parser_errposition(cxt->pstate,
  									constraint->location)));
  
--- 599,612 ----
  static void
  transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
  {
! 	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("primary key, unique, exclusion, or foreign key constraints are not supported on foreign tables"),
  				 parser_errposition(cxt->pstate,
  									constraint->location)));
  
*** a/src/include/commands/vacuum.h
--- b/src/include/commands/vacuum.h
***************
*** 140,145 **** extern int	vacuum_multixact_freeze_min_age;
--- 140,154 ----
  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,180 **** extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
  				BufferAccessStrategy bstrategy);
  
  /* in commands/analyze.c */
! extern void analyze_rel(Oid relid, VacuumStmt *vacstmt,
  			BufferAccessStrategy bstrategy);
  extern bool std_typanalyze(VacAttrStats *stats);
  extern double anl_random_fract(void);
--- 183,191 ----
  				BufferAccessStrategy bstrategy);
  
  /* in commands/analyze.c */
! extern void analyze_rel(Oid relid,
! 			VacuumStmt *vacstmt,
! 			VacuumMode vacmode,
  			BufferAccessStrategy bstrategy);
  extern bool std_typanalyze(VacAttrStats *stats);
  extern double anl_random_fract(void);
*** a/src/include/foreign/fdwapi.h
--- b/src/include/foreign/fdwapi.h
***************
*** 31,36 **** typedef void (*GetForeignPaths_function) (PlannerInfo *root,
--- 31,42 ----
  													  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,
***************
*** 120,125 **** typedef struct FdwRoutine
--- 126,132 ----
  	/* Functions for scanning foreign tables */
  	GetForeignRelSize_function GetForeignRelSize;
  	GetForeignPaths_function GetForeignPaths;
+ 	ReparameterizeForeignPath_function ReparameterizeForeignPath;
  	GetForeignPlan_function GetForeignPlan;
  	BeginForeignScan_function BeginForeignScan;
  	IterateForeignScan_function IterateForeignScan;
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 810,815 **** typedef struct RangeTblEntry
--- 810,816 ----
  	Alias	   *eref;			/* expanded reference names */
  	bool		lateral;		/* subquery, function, or values is LATERAL? */
  	bool		inh;			/* inheritance requested? */
+ 	bool		hasForeign;		/* does inheritance include foreign tables? */
  	bool		inFromCl;		/* present in FROM clause? */
  	AclMode		requiredPerms;	/* bitmask of required access permissions */
  	Oid			checkAsUser;	/* if valid, check access as this role */
*** a/src/test/regress/expected/foreign_data.out
--- b/src/test/regress/expected/foreign_data.out
***************
*** 748,763 **** 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 DROP CONSTRAINT no_const;               -- ERROR
! ERROR:  "ft1" is not a table
  ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
! ERROR:  "ft1" is not a table
! ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
! ERROR:  "ft1" is not a table
  ALTER FOREIGN TABLE ft1 SET WITH OIDS;                          -- ERROR
  ERROR:  "ft1" is not a table
  ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
--- 748,759 ----
  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);
  ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const;               -- ERROR
! ERROR:  constraint "no_const" of relation "ft1" does not exist
  ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
! NOTICE:  constraint "no_const" of relation "ft1" does not exist, skipping
! ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_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;
***************
*** 832,837 **** ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME c1 TO foreign_column_1;
--- 828,931 ----
  NOTICE:  relation "doesnt_exist_ft1" does not exist, skipping
  ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME TO foreign_table_1;
  NOTICE:  relation "doesnt_exist_ft1" does not exist, skipping
+ -- Table inheritance
+ CREATE TABLE pt1 (ff1 integer);
+ CREATE VIEW view1 AS SELECT 1;
+ ALTER TABLE view1 INHERIT pt1;                                  -- ERROR
+ ERROR:  "view1" is not a table or foreign table
+ CREATE FOREIGN TABLE ct1 () INHERITS (pt1)
+ SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+ CREATE FOREIGN TABLE ct2 (ff1 integer)
+ SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+ ALTER FOREIGN TABLE ct2 INHERIT pt1;
+ \d+ pt1
+                          Table "public.pt1"
+  Column |  Type   | Modifiers | Storage | Stats target | Description 
+ --------+---------+-----------+---------+--------------+-------------
+  ff1    | integer |           | plain   |              | 
+ Child tables: ct1,
+               ct2
+ 
+ \d+ ct1
+                             Foreign table "public.ct1"
+  Column |  Type   | Modifiers | FDW Options | Storage | Stats target | Description 
+ --------+---------+-----------+-------------+---------+--------------+-------------
+  ff1    | integer |           |             | plain   |              | 
+ Server: s0
+ FDW Options: (delimiter ',', quote '"', "be quoted" 'value')
+ Inherits: pt1
+ 
+ \d+ ct2
+                             Foreign table "public.ct2"
+  Column |  Type   | Modifiers | FDW Options | Storage | Stats target | Description 
+ --------+---------+-----------+-------------+---------+--------------+-------------
+  ff1    | integer |           |             | plain   |              | 
+ Server: s0
+ FDW Options: (delimiter ',', quote '"', "be quoted" 'value')
+ Inherits: pt1
+ 
+ ALTER FOREIGN TABLE ct1 NO INHERIT pt1;
+ ALTER FOREIGN TABLE ct2 NO INHERIT pt1;
+ ALTER TABLE pt1 ADD CONSTRAINT pt1chk1 CHECK (ff1 > 0) NO INHERIT;
+ ALTER TABLE pt1 ADD CONSTRAINT pt1chk2 CHECK (ff1 > 10);
+ -- connoinherit should be true for NO INHERIT constraint
+ SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname = 'pt1' order by 1,2;
+  relname | conname | contype | conislocal | coninhcount | connoinherit 
+ ---------+---------+---------+------------+-------------+--------------
+  pt1     | pt1chk1 | c       | t          |           0 | t
+  pt1     | pt1chk2 | c       | t          |           0 | f
+ (2 rows)
+ 
+ -- child does not inherit NO INHERIT constraints
+ CREATE FOREIGN TABLE ct3 () INHERITS (pt1)
+ SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+ CREATE FOREIGN TABLE ct4 (ff1 integer)
+ SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+ -- child must have parent's INHERIT constraints
+ ALTER FOREIGN TABLE ct4 INHERIT pt1;                            -- ERROR
+ ERROR:  child table is missing constraint "pt1chk2"
+ ALTER TABLE ct4 ADD CONSTRAINT pt1chk2 CHECK (ff1 > 10);
+ ALTER FOREIGN TABLE ct4 INHERIT pt1;
+ \d+ pt1
+                          Table "public.pt1"
+  Column |  Type   | Modifiers | Storage | Stats target | Description 
+ --------+---------+-----------+---------+--------------+-------------
+  ff1    | integer |           | plain   |              | 
+ Check constraints:
+     "pt1chk1" CHECK (ff1 > 0) NO INHERIT
+     "pt1chk2" CHECK (ff1 > 10)
+ Child tables: ct3,
+               ct4
+ 
+ \d+ ct3
+                             Foreign table "public.ct3"
+  Column |  Type   | Modifiers | FDW Options | Storage | Stats target | Description 
+ --------+---------+-----------+-------------+---------+--------------+-------------
+  ff1    | integer |           |             | plain   |              | 
+ Check constraints:
+     "pt1chk2" CHECK (ff1 > 10)
+ Server: s0
+ FDW Options: (delimiter ',', quote '"', "be quoted" 'value')
+ Inherits: pt1
+ 
+ \d+ ct4
+                             Foreign table "public.ct4"
+  Column |  Type   | Modifiers | FDW Options | Storage | Stats target | Description 
+ --------+---------+-----------+-------------+---------+--------------+-------------
+  ff1    | integer |           |             | plain   |              | 
+ Check constraints:
+     "pt1chk2" CHECK (ff1 > 10)
+ Server: s0
+ FDW Options: (delimiter ',', quote '"', "be quoted" 'value')
+ Inherits: pt1
+ 
+ DROP VIEW view1;
+ DROP FOREIGN TABLE ct1;
+ DROP FOREIGN TABLE ct2;
+ DROP TABLE pt1 CASCADE;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to foreign table ct3
+ drop cascades to foreign table ct4
  -- Information schema
  SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
   foreign_data_wrapper_catalog | foreign_data_wrapper_name | authorization_identifier | library_name | foreign_data_wrapper_language 
*** a/src/test/regress/sql/foreign_data.sql
--- b/src/test/regress/sql/foreign_data.sql
***************
*** 314,323 **** 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 DROP CONSTRAINT no_const;               -- ERROR
  ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
! ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
  ALTER FOREIGN TABLE ft1 SET WITH OIDS;                          -- ERROR
  ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
  ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@');
--- 314,323 ----
  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);
  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_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 '@');
***************
*** 326,331 **** ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column;
--- 326,332 ----
  ALTER FOREIGN TABLE ft1 DROP COLUMN c9;
  ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema;
  ALTER FOREIGN TABLE ft1 SET TABLESPACE ts;                      -- ERROR
+ 
  ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
  ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
  \d foreign_schema.foreign_table_1
***************
*** 356,361 **** ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 SET SCHEMA foreign_schema;
--- 357,399 ----
  ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME c1 TO foreign_column_1;
  ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME TO foreign_table_1;
  
+ -- Table inheritance
+ CREATE TABLE pt1 (ff1 integer);
+ CREATE VIEW view1 AS SELECT 1;
+ ALTER TABLE view1 INHERIT pt1;                                  -- ERROR
+ CREATE FOREIGN TABLE ct1 () INHERITS (pt1)
+ SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+ CREATE FOREIGN TABLE ct2 (ff1 integer)
+ SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+ ALTER FOREIGN TABLE ct2 INHERIT pt1;
+ \d+ pt1
+ \d+ ct1
+ \d+ ct2
+ 
+ ALTER FOREIGN TABLE ct1 NO INHERIT pt1;
+ ALTER FOREIGN TABLE ct2 NO INHERIT pt1;
+ ALTER TABLE pt1 ADD CONSTRAINT pt1chk1 CHECK (ff1 > 0) NO INHERIT;
+ ALTER TABLE pt1 ADD CONSTRAINT pt1chk2 CHECK (ff1 > 10);
+ -- connoinherit should be true for NO INHERIT constraint
+ SELECT pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit FROM pg_class AS pc INNER JOIN pg_constraint AS pgc ON (pgc.conrelid = pc.oid) WHERE pc.relname = 'pt1' order by 1,2;
+ -- child does not inherit NO INHERIT constraints
+ CREATE FOREIGN TABLE ct3 () INHERITS (pt1)
+ SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+ CREATE FOREIGN TABLE ct4 (ff1 integer)
+ SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+ -- child must have parent's INHERIT constraints
+ ALTER FOREIGN TABLE ct4 INHERIT pt1;                            -- ERROR
+ ALTER TABLE ct4 ADD CONSTRAINT pt1chk2 CHECK (ff1 > 10);
+ ALTER FOREIGN TABLE ct4 INHERIT pt1;
+ \d+ pt1
+ \d+ ct3
+ \d+ ct4
+ 
+ DROP VIEW view1;
+ DROP FOREIGN TABLE ct1;
+ DROP FOREIGN TABLE ct2;
+ DROP TABLE pt1 CASCADE;
+ 
  -- Information schema
  
  SELECT * FROM information_schema.foreign_data_wrappers ORDER BY 1, 2;
