(2014/03/18 18:38), Kyotaro HORIGUCHI wrote:
By the way, Can I have a simple script to build an environment to
run this on?

I built test environment and ran the simple test using
postgres_fdw and got parameterized path from v3 patch on the
following operation as shown there, and v6 also gives one, but I
haven't seen the reparameterization of v6 patch work.

# How could I think to have got it work before?

Do you have any idea to make postgreReparameterizeForeignPath on
foreign (child) tables works effectively?

=# explain analyze select pu1.*
     from pu1 join rpu1 on (pu1.c = rpu1.c) where pu1.a = 3;

ISTM postgresReparameterizeForeignPath() cannot be called in this query in principle. Here is a simple example for the case where the use_remote_estimate option is true:

# On mydatabase

mydatabase=# CREATE TABLE mytable (id INTEGER, x INTEGER);
CREATE TABLE
mydatabase=# INSERT INTO mytable SELECT x, x FROM generate_series(0, 9999) x;
INSERT 0 10000

# On postgres

postgres=# CREATE TABLE inttable (id INTEGER);
CREATE TABLE
postgres=# INSERT INTO inttable SELECT x FROM generate_series(0, 9999) x;
INSERT 0 10000
postgres=# ANALYZE inttable;
ANALYZE

postgres=# CREATE TABLE patest0 (id INTEGER, x INTEGER);
CREATE TABLE
postgres=# CREATE TABLE patest1 () INHERITS (patest0);
CREATE TABLE
postgres=# INSERT INTO patest1 SELECT x, x FROM generate_series(0, 9999) x;
INSERT 0 10000
postgres=# CREATE INDEX patest1_id_idx ON patest1(id);
CREATE INDEX
postgres=# CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'localhost', dbname 'mydatabase');
CREATE SERVER
postgres=# CREATE USER MAPPING FOR PUBLIC SERVER myserver OPTIONS (user 'pgsql');
CREATE USER MAPPING
postgres=# CREATE FOREIGN TABLE patest2 () INHERITS (patest0) SERVER myserver OPTIONS (table_name 'mytable');
CREATE FOREIGN TABLE
postgres=# ANALYZE patest0;
ANALYZE
postgres=# ANALYZE patest1;
ANALYZE
postgres=# ANALYZE patest2;
ANALYZE
postgres=# EXPLAIN VERBOSE SELECT * FROM patest0 join (SELECT id FROM inttable LIMIT 1) ss ON patest0.id = ss.id;
                                           QUERY PLAN
-------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.00..478.36 rows=2 width=12)
   Output: patest0.id, patest0.x, inttable.id
   ->  Limit  (cost=0.00..0.01 rows=1 width=4)
         Output: inttable.id
-> Seq Scan on public.inttable (cost=0.00..145.00 rows=10000 width=4)
               Output: inttable.id
   ->  Append  (cost=0.00..478.31 rows=3 width=8)
         ->  Seq Scan on public.patest0  (cost=0.00..0.00 rows=1 width=8)
               Output: patest0.id, patest0.x
               Filter: (inttable.id = patest0.id)
-> Index Scan using patest1_id_idx on public.patest1 (cost=0.29..8.30 rows=1 width=8)
               Output: patest1.id, patest1.x
               Index Cond: (patest1.id = inttable.id)
-> Foreign Scan on public.patest2 (cost=100.00..470.00 rows=1 width=8)
               Output: patest2.id, patest2.x
Remote SQL: SELECT id, x FROM public.mytable WHERE (($1::integer = id))
 Planning time: 0.233 ms
(17 rows)

I revised the patch. Patche attached, though I plan to update the documentation further early next week.

Thanks,

Best regards,
Etsuro Fujita
*** a/contrib/file_fdw/file_fdw.c
--- b/contrib/file_fdw/file_fdw.c
***************
*** 117,122 **** static void fileGetForeignRelSize(PlannerInfo *root,
--- 117,126 ----
  static void fileGetForeignPaths(PlannerInfo *root,
                                        RelOptInfo *baserel,
                                        Oid foreigntableid);
+ static ForeignPath *fileReparameterizeForeignPath(PlannerInfo *root,
+                                                                               
                  RelOptInfo *baserel,
+                                                                               
                  Path *path,
+                                                                               
                  Relids required_outer);
  static ForeignScan *fileGetForeignPlan(PlannerInfo *root,
                                   RelOptInfo *baserel,
                                   Oid foreigntableid,
***************
*** 145,150 **** static bool check_selective_binary_conversion(RelOptInfo 
*baserel,
--- 149,155 ----
  static void estimate_size(PlannerInfo *root, RelOptInfo *baserel,
                          FileFdwPlanState *fdw_private);
  static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
+                          List *join_conds,
                           FileFdwPlanState *fdw_private,
                           Cost *startup_cost, Cost *total_cost);
  static int file_acquire_sample_rows(Relation onerel, int elevel,
***************
*** 163,168 **** file_fdw_handler(PG_FUNCTION_ARGS)
--- 168,174 ----
  
        fdwroutine->GetForeignRelSize = fileGetForeignRelSize;
        fdwroutine->GetForeignPaths = fileGetForeignPaths;
+       fdwroutine->ReparameterizeForeignPath = fileReparameterizeForeignPath;
        fdwroutine->GetForeignPlan = fileGetForeignPlan;
        fdwroutine->ExplainForeignScan = fileExplainForeignScan;
        fdwroutine->BeginForeignScan = fileBeginForeignScan;
***************
*** 517,523 **** fileGetForeignPaths(PlannerInfo *root,
                                                                                
  (Node *) columns));
  
        /* Estimate costs */
!       estimate_costs(root, baserel, fdw_private,
                                   &startup_cost, &total_cost);
  
        /*
--- 523,530 ----
                                                                                
  (Node *) columns));
  
        /* Estimate costs */
!       estimate_costs(root, baserel,
!                                  NIL, fdw_private,
                                   &startup_cost, &total_cost);
  
        /*
***************
*** 542,547 **** fileGetForeignPaths(PlannerInfo *root,
--- 549,588 ----
  }
  
  /*
+  * fileReparameterizeForeignPath
+  *            Attempt to modify a given path to have greater parameterization
+  */
+ static ForeignPath *
+ fileReparameterizeForeignPath(PlannerInfo *root,
+                                                         RelOptInfo *baserel,
+                                                         Path *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 estimate */            
+       estimate_costs(root, baserel,
+                                  param_info->ppi_clauses,
+                                  fdw_private,
+                                  &startup_cost, &total_cost);
+ 
+       /* Make a new path */
+       return create_foreignscan_path(root, baserel,
+                                                                  
param_info->ppi_rows,
+                                                                  startup_cost,
+                                                                  total_cost,
+                                                                  NIL,         
/* no pathkeys */
+                                                                  
required_outer,
+                                                                  
((ForeignPath *) path)->fdw_private);
+ }
+ 
+ /*
   * fileGetForeignPlan
   *            Create a ForeignScan plan node for scanning the foreign table
   */
***************
*** 970,981 **** estimate_size(PlannerInfo *root, RelOptInfo *baserel,
--- 1011,1024 ----
   */
  static void
  estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
+                          List *join_conds,
                           FileFdwPlanState *fdw_private,
                           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;
  
        /*
***************
*** 986,993 **** 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;
  }
--- 1029,1039 ----
         */
        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/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
***************
*** 241,246 **** static void postgresGetForeignRelSize(PlannerInfo *root,
--- 241,250 ----
  static void postgresGetForeignPaths(PlannerInfo *root,
                                                RelOptInfo *baserel,
                                                Oid foreigntableid);
+ static ForeignPath *postgresReparameterizeForeignPath(PlannerInfo *root,
+                                                                               
                          RelOptInfo *baserel,
+                                                                               
                          Path *path,
+                                                                               
                          Relids required_outer);
  static ForeignScan *postgresGetForeignPlan(PlannerInfo *root,
                                           RelOptInfo *baserel,
                                           Oid foreigntableid,
***************
*** 342,347 **** 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;
***************
*** 729,734 **** postgresGetForeignPaths(PlannerInfo *root,
--- 734,780 ----
  }
  
  /*
+  * postgresReparameterizeForeignPath
+  *            Attempt to modify a given path to have greater parameterization
+  */
+ static ForeignPath *
+ postgresReparameterizeForeignPath(PlannerInfo *root,
+                                                                 RelOptInfo 
*baserel,
+                                                                 Path *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 estimate */
+       estimate_path_cost_size(root, baserel,
+                                                       param_info->ppi_clauses,
+                                                       &rows, &width,
+                                                       &startup_cost, 
&total_cost);
+ 
+       /*
+        * ppi_rows currently won't get looked at by anything, but still we
+        * may as well ensure that it matches our idea of the rowcount.
+        */
+       param_info->ppi_rows = rows;
+ 
+       /* Make a 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
   */
***************
*** 1775,1785 **** 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;
--- 1821,1828 ----
        }
        else
        {
!               Selectivity join_sel;
!               QualCost        join_cost;
  
                /* Use rows/width estimates made by set_baserel_size_estimates. 
*/
                rows = baserel->rows;
***************
*** 1792,1808 **** 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;
--- 1835,1863 ----
                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/doc/src/sgml/ddl.sgml
--- b/doc/src/sgml/ddl.sgml
***************
*** 258,263 **** CREATE TABLE products (
--- 258,274 ----
     even if the value came from the default value definition.
    </para>
  
+   <note>
+    <para>
+     Note that constraints can be defined on foreign tables too, but such
+     constraints are not enforced on insert or update.  Those constraints are
+     "assertive", and work only to tell planner that some kind of optimization
+     such as constraint exclusion can be considerd.  This seems useless, but
+     allows us to use foriegn table as child table (see
+     <xref linkend="ddl-inherit">) to off-load to multiple servers.
+    </para>
+   </note>
+ 
    <sect2 id="ddl-constraints-check-constraints">
     <title>Check Constraints</title>
  
***************
*** 2017,2024 **** CREATE TABLE capitals (
    </para>
  
    <para>
!    In <productname>PostgreSQL</productname>, a table can inherit from
!    zero or more other tables, and a query can reference either all
     rows of a table or all rows of a table plus all of its descendant tables.
     The latter behavior is the default.
     For example, the following query finds the names of all cities,
--- 2028,2035 ----
    </para>
  
    <para>
!    In <productname>PostgreSQL</productname>, a table or foreign table can
!    inherit from zero or more other tables, and a query can reference either 
all
     rows of a table or all rows of a table plus all of its descendant tables.
     The latter behavior is the default.
     For example, the following query finds the names of all cities,
*** a/doc/src/sgml/ref/alter_foreign_table.sgml
--- b/doc/src/sgml/ref/alter_foreign_table.sgml
***************
*** 42,47 **** ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable 
class="PARAMETER">name</replaceab
--- 42,49 ----
      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>'] [, ... ])
+     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>
***************
*** 178,183 **** ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable 
class="PARAMETER">name</replaceab
--- 180,205 ----
     </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 a plain 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>OPTIONS ( [ ADD | SET | DROP ] <replaceable 
class="PARAMETER">option</replaceable> ['<replaceable 
class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
      <listitem>
       <para>
***************
*** 306,311 **** ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable 
class="PARAMETER">name</replaceab
--- 328,343 ----
         </para>
        </listitem>
       </varlistentry>
+ 
+      <varlistentry>
+       <term><replaceable class="PARAMETER">parent_name</replaceable></term>
+       <listitem>
+        <para>
+         A parent table to associate or de-associate with this foreign table.
+         The parent table must be a plain table.
+        </para>
+       </listitem>
+      </varlistentry>
      </variablelist>
   </refsect1>
  
*** a/doc/src/sgml/ref/create_foreign_table.sgml
--- b/doc/src/sgml/ref/create_foreign_table.sgml
***************
*** 22,27 **** CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable 
class="PARAMETER">table_name
--- 22,28 ----
      <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> [ ... ] ]
      [, ... ]
  ] )
+ [ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
    SERVER <replaceable class="parameter">server_name</replaceable>
  [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable 
class="PARAMETER">value</replaceable>' [, ... ] ) ]
  
***************
*** 159,164 **** CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable 
class="PARAMETER">table_name
--- 160,177 ----
     </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 a plain 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>
*** a/src/backend/commands/analyze.c
--- b/src/backend/commands/analyze.c
***************
*** 82,90 **** int                      default_statistics_target = 100;
  
  /* A few variables that don't seem worth passing around as parameters */
  static MemoryContext anl_context = NULL;
  static BufferAccessStrategy vac_strategy;
  
- 
  static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
                           AcquireSampleRowsFunc acquirefunc, BlockNumber 
relpages,
                           bool inh, int elevel);
--- 82,90 ----
  
  /* 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;
  
  static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
                           AcquireSampleRowsFunc acquirefunc, BlockNumber 
relpages,
                           bool inh, int elevel);
***************
*** 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;
--- 115,123 ----
   *    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)
--- 131,137 ----
                elevel = DEBUG2;
  
        /* Set up static variables */
+       vac_mode = vacmode;
        vac_strategy = bstrategy;
  
        /*
***************
*** 1452,1459 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
--- 1455,1464 ----
  {
        List       *tableOIDs;
        Relation   *rels;
+       AcquireSampleRowsFunc *acquirefunc;
        double     *relblocks;
        double          totalblocks;
+       Relation        saved_rel;
        int                     numrows,
                                nrels,
                                i;
***************
*** 1486,1491 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
--- 1491,1498 ----
         * 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++;
        }
--- 1514,1553 ----
                }
  
                rels[nrels] = childrel;
! 
!               if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
!               {
!                       FdwRoutine *fdwroutine;
!                       BlockNumber relpages = 0;
!                       bool            ok = false;
! 
!                       /* Ignore unless analyzing a specific inheritance tree 
*/
!                       if (vac_mode != VAC_MODE_SINGLE)
!                               return 0;
! 
!                       /* Check whether the FDW supports analysis */
!                       fdwroutine = GetFdwRoutineForRelation(childrel, false);
!                       if (fdwroutine->AnalyzeForeignTable != NULL)
!                               ok = fdwroutine->AnalyzeForeignTable(childrel,
!                                                                               
                         &acquirefunc[nrels],
!                                                                               
                         &relpages);
!                       if (!ok)
!                       {
!                               /* Give up if the FDW doesn't support analysis 
*/
!                               ereport(WARNING,
!                               (errmsg("skipping \"%s\" inheritance tree --- 
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,
--- 1565,1571 ----
        {
                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 &&
--- 1581,1592 ----
                                                        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
***************
*** 311,317 **** 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,
--- 311,318 ----
  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,
***************
*** 467,476 **** 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,
--- 468,473 ----
***************
*** 3019,3042 **** 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;
--- 3016,3043 ----
                         * rules.
                         */
                        ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | 
ATT_FOREIGN_TABLE);
!                       /* Recurses 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);
!                       /* Recurses 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);
!                       /* Recurses 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 */
!                       /* Recurses 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;
***************
*** 3049,3055 **** 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;
--- 3050,3057 ----
                        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;
***************
*** 3067,3073 **** 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)
--- 3069,3075 ----
                        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)
***************
*** 3081,3087 **** 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)
--- 3083,3089 ----
                        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)
***************
*** 3149,3161 **** 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;
                        break;
                case AT_ValidateConstraint:             /* VALIDATE CONSTRAINT 
*/
--- 3151,3169 ----
                        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 | ATT_FOREIGN_TABLE);
                        pass = AT_PASS_MISC;
                        break;
                case AT_ValidateConstraint:             /* VALIDATE CONSTRAINT 
*/
***************
*** 3184,3190 **** 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);
--- 3192,3197 ----
***************
*** 4125,4131 **** 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
--- 4132,4139 ----
   */
  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
***************
*** 4153,4160 **** 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);
                }
        }
--- 4161,4172 ----
                                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);
                }
        }
***************
*** 4444,4450 **** 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);
  
--- 4456,4462 ----
  
        /* 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);
  
***************
*** 4740,4745 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, 
Relation rel,
--- 4752,4763 ----
  
                /* 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 
inheritance tree \"%s\" because it contains foreign table \"%s\"",
+                                                       
RelationGetRelationName(rel),
+                                                       
RelationGetRelationName(childrel))));
                CheckTableNotInUse(childrel, "ALTER TABLE");
  
                /* Find or create work queue entry for this table */
***************
*** 5340,5346 **** 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
--- 5358,5364 ----
  
        /* 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
***************
*** 5732,5738 **** 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
--- 5750,5763 ----
  
        /* 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 on 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
***************
*** 5743,5751 **** 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 */
--- 5768,5784 ----
         * 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 */
***************
*** 7225,7231 **** 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);
  
--- 7258,7264 ----
  
        /* 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);
  
***************
*** 7560,7566 **** 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,
--- 7593,7602 ----
         * alter would put them out of step.
         */
        if (recurse)
!       {
!               /* Recurses 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
***************
*** 98,103 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
--- 98,104 ----
           BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel)
  {
        const char *stmttype;
+       VacuumMode      vacmode;
        volatile bool in_outer_xact,
                                use_own_xacts;
        List       *relations;
***************
*** 146,151 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
--- 147,166 ----
                                                                                
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)
+               vacmode = VAC_MODE_AUTOVACUUM;
+       else
+       {
+               if (!vacstmt->relation)
+                       vacmode = VAC_MODE_ALL;
+               else
+                       vacmode = VAC_MODE_SINGLE;
+       }
+ 
+       /*
         * If caller didn't give us a buffer strategy object, make one in the
         * cross-transaction memory context.
         */
***************
*** 248,254 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
                                        
PushActiveSnapshot(GetTransactionSnapshot());
                                }
  
!                               analyze_rel(relid, vacstmt, vac_strategy);
  
                                if (use_own_xacts)
                                {
--- 263,269 ----
                                        
PushActiveSnapshot(GetTransactionSnapshot());
                                }
  
!                               analyze_rel(relid, vacstmt, vacmode, 
vac_strategy);
  
                                if (use_own_xacts)
                                {
*** a/src/backend/optimizer/prep/prepunion.c
--- b/src/backend/optimizer/prep/prepunion.c
***************
*** 1337,1347 **** 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);
--- 1337,1348 ----
                /*
                 * 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);
*** 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"
***************
*** 2062,2067 **** reparameterize_path(PlannerInfo *root, Path *path,
--- 2063,2075 ----
                case T_SubqueryScan:
                        return create_subqueryscan_path(root, rel, 
path->pathkeys,
                                                                                
        required_outer);
+               case T_ForeignScan:
+                       if (rel->fdwroutine->ReparameterizeForeignPath != NULL)
+                               return (Path *)
+                                       
rel->fdwroutine->ReparameterizeForeignPath(root,
+                                                                               
                                           rel,
+                                                                               
                                           path,
+                                                                               
                                           required_outer);
                default:
                        break;
        }
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 4209,4240 **** 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;
                                }
                ;
--- 4209,4240 ----
  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,189 ----
                                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,41 ----
                                                                                
                          RelOptInfo *baserel,
                                                                                
                          Oid foreigntableid);
  
+ typedef ForeignPath *(*ReparameterizeForeignPath_function) (PlannerInfo *root,
+                                                                               
                                 RelOptInfo *baserel,
+                                                                               
                                        Path *path,
+                                                                               
                          Relids required_outer);
+ 
  typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root,
                                                                                
                                 RelOptInfo *baserel,
                                                                                
                                  Oid foreigntableid,
***************
*** 117,122 **** typedef struct FdwRoutine
--- 122,128 ----
        /* 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/test/regress/expected/foreign_data.out
--- b/src/test/regress/expected/foreign_data.out
***************
*** 750,765 **** 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;
--- 750,761 ----
  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;
*** 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 
'@');
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to