Re: inherit support for foreign tables - Mailing list pgsql-hackers

From Kyotaro HORIGUCHI
Subject Re: inherit support for foreign tables
Date
Msg-id 20140311.140751.146429295.horiguchi.kyotaro@lab.ntt.co.jp
Whole thread Raw
In response to Re: inherit support for foreign tables  (Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp>)
Responses Re: inherit support for foreign tables  (Etsuro Fujita <fujita.etsuro@lab.ntt.co.jp>)
List pgsql-hackers
Hello, 

> This seems far better than silently performing the command,
> except for the duplicated message:( New bitmap might required to
> avoid the duplication..

I rewrote it in more tidy way. ATController collects all affected
tables on ATRewriteCatalogs as first stage, then emit NOTICE
message according to the affected relations list. The message
looks like,

| =# alter table passwd alter column uname set default 'hoge';
| NOTICE:  This command affects 2 foreign tables: cf1, cf2
| ALTER TABLE

Do you feel this too large or complicated? I think so a bit..

regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 8ace8bd..b4e53c1 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -258,6 +258,17 @@ CREATE TABLE products (   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,8 +2028,8 @@ 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
+   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
tableplus all of its descendant tables.   The latter behavior is the default.   For example, the following query finds
thenames of all cities,
 
diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml
index 4d8cfc5..f7a382e 100644
--- a/doc/src/sgml/ref/alter_foreign_table.sgml
+++ b/doc/src/sgml/ref/alter_foreign_table.sgml
@@ -42,6 +42,8 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab    ALTER [ COLUMN ]
<replaceableclass="PARAMETER">column_name</replaceable> SET ( <replaceable
class="PARAMETER">attribute_option</replaceable>= <replaceable class="PARAMETER">value</replaceable> [, ... ] )
ALTER[ COLUMN ] <replaceable class="PARAMETER">column_name</replaceable> RESET ( <replaceable
class="PARAMETER">attribute_option</replaceable>[, ... ] )    ALTER [ COLUMN ] <replaceable
class="PARAMETER">column_name</replaceable>OPTIONS ( [ ADD | SET | DROP ] <replaceable
class="PARAMETER">option</replaceable>['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
 
+    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,6 +180,26 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab   </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>
['<replaceableclass="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>    <listitem>     <para>
 
@@ -306,6 +328,16 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab       </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>
diff --git a/doc/src/sgml/ref/create_foreign_table.sgml b/doc/src/sgml/ref/create_foreign_table.sgml
index 06a7087..cc11dee 100644
--- a/doc/src/sgml/ref/create_foreign_table.sgml
+++ b/doc/src/sgml/ref/create_foreign_table.sgml
@@ -22,6 +22,7 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name    <replaceable
class="PARAMETER">column_name</replaceable><replaceable class="PARAMETER">data_type</replaceable> [ OPTIONS (
<replaceableclass="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,6 +160,18 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name   </varlistentry>
 <varlistentry>
 
+    <term><replaceable class="PARAMETER">parent_table</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing table from which the new foreign table
+      automatically inherits all columns.  The specified parent table
+      must be 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>
diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index f263b42..b4a084c 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -256,6 +256,48 @@ has_subclass(Oid relationId)/*
+ * Check wether the inheritance tree contains foreign table(s).
+ */
+bool
+contains_foreign(Oid parentrelId, LOCKMODE lockmode)
+{
+    bool        result = false;
+    List       *tableOIDs;
+    ListCell   *lc;
+
+    /* Find all members of the inheritance tree */
+    tableOIDs = find_all_inheritors(parentrelId, lockmode, NULL);
+
+    /* 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, lockmode);
+    }
+
+    return result;
+}
+
+
+/* * Given two type OIDs, determine whether the first is a complex type * (class type) that inherits from the second.
*/
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index a04adea..bb7bdf4 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -115,7 +115,8 @@ static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull); *    analyze_rel() --
analyzeone relation */void
 
-analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
+analyze_rel(Oid relid, VacuumStmt *vacstmt, VacuumMode vacmode,
+            BufferAccessStrategy bstrategy){    Relation    onerel;    int            elevel;
@@ -270,7 +271,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)    /*     * If there
arechild tables, do recursive ANALYZE.     */
 
-    if (onerel->rd_rel->relhassubclass)
+    if (onerel->rd_rel->relhassubclass &&
+        (vacmode == VAC_MODE_SINGLE ||
+         !contains_foreign(RelationGetRelid(onerel), AccessShareLock)))        do_analyze_rel(onerel, vacstmt,
acquirefunc,relpages, true, elevel);    /*
 
@@ -1452,12 +1455,15 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,{    List       *tableOIDs;
Relation  *rels;
 
+    AcquireSampleRowsFunc *acquirefunc;    double       *relblocks;    double        totalblocks;
+    Relation    saved_rel;    int            numrows,                nrels,                i;    ListCell   *lc;
+    bool        isAnalyzable = true;    /*     * Find all members of inheritance set.  We only need AccessShareLock
on
@@ -1486,6 +1492,8 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,     * BlockNumber, so we use double
arithmetic.    */    rels = (Relation *) palloc(list_length(tableOIDs) * sizeof(Relation));
 
+    acquirefunc = (AcquireSampleRowsFunc *) palloc(list_length(tableOIDs)
+                                            * sizeof(AcquireSampleRowsFunc));    relblocks = (double *)
palloc(list_length(tableOIDs)* sizeof(double));    totalblocks = 0;    nrels = 0;
 
@@ -1507,12 +1515,59 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,        }        rels[nrels] =
childrel;
-        relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel);
+
+        if (childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+        {
+            acquirefunc[nrels] = acquire_sample_rows;
+            relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel);
+        }
+        else
+        {
+            /*
+             * For a foreign table, call the FDW's hook function to see whether
+             * it supports analysis.
+             */
+            FdwRoutine *fdwroutine;
+            BlockNumber relpages = 0;
+            bool        ok = false;
+
+            fdwroutine = GetFdwRoutineForRelation(childrel, false);
+            if (fdwroutine->AnalyzeForeignTable != NULL)
+                ok = fdwroutine->AnalyzeForeignTable(childrel,
+                                                     &acquirefunc[nrels],
+                                                     &relpages);
+            if (!ok)
+            {
+                isAnalyzable = false;
+                break;
+            }
+
+            relblocks[nrels] = (double) relpages;
+        }
+        totalblocks += relblocks[nrels];        nrels++;    }    /*
+     * If there is at least one foreign table that cannot be analyzed, give up.
+     */
+    if (!isAnalyzable)
+    {
+        ereport(WARNING,
+                (errmsg("skipping \"%s\" inheritance tree --- cannot analyze foreign table \"%s\"",
+                        RelationGetRelationName(onerel),
+                        RelationGetRelationName(rels[nrels]))));
+        for (i = 0; i < nrels; i++)
+        {
+            Relation    childrel = rels[i];
+
+            heap_close(childrel, NoLock);
+        }
+        return 0;
+    }
+
+    /*     * Now sample rows from each relation, proportionally to its fraction of     * the total block count.  (This
mightbe less than desirable if the child     * rels have radically different free-space percentages, but it's not
 
@@ -1525,6 +1580,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,    {        Relation    childrel =
rels[i];       double        childblocks = relblocks[i];
 
+        AcquireSampleRowsFunc childacquirefunc = acquirefunc[i];        if (childblocks > 0)        {
@@ -1540,12 +1596,12 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,                            tdrows;
            /* Fetch a random sample of the child's rows */
 
-                childrows = acquire_sample_rows(childrel,
-                                                elevel,
-                                                rows + numrows,
-                                                childtargrows,
-                                                &trows,
-                                                &tdrows);
+                childrows = childacquirefunc(childrel,
+                                             elevel,
+                                             rows + numrows,
+                                             childtargrows,
+                                             &trows,
+                                             &tdrows);                /* We may need to convert from child's rowtype
toparent's */                if (childrows > 0 &&
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 25f01e5..6859ff0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -299,26 +299,30 @@ static void validateForeignKeyConstraint(char *conname,static void
createForeignKeyTriggers(Relationrel, Oid refRelOid,                         Constraint *fkconstraint,
      Oid constraintOid, Oid indexOid);
 
+static void PrintForeignNotice(List *affected, Relation rel);static void ATController(Relation rel, List *cmds, bool
recurse,LOCKMODE lockmode);static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,          bool
recurse,bool recursing, LOCKMODE lockmode);
 
-static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode);
-static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
-          AlterTableCmd *cmd, LOCKMODE lockmode);
+static void ATRewriteCatalogs(List **wqueue, List **affectd, LOCKMODE lockmode);
+static void ATExecCmd(List **wqueue, List **affected, AlteredTableInfo *tab,
+                      Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode);static void ATRewriteTables(List **wqueue,
LOCKMODElockmode);static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);static
AlteredTableInfo*ATGetQueueEntry(List **wqueue, Relation rel);
 
+static void ATAddAffectedRelid(List **affected, Oid relid);static void ATSimplePermissions(Relation rel, int
allowed_targets);staticvoid ATWrongRelkindError(Relation rel, int allowed_targets);static void ATSimpleRecursion(List
**wqueue,Relation rel,
 
-                  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
+                  AlterTableCmd *cmd, bool recurse,
+                  bool include_foreign, LOCKMODE lockmode);static void ATTypedTableRecursion(List **wqueue, Relation
rel,AlterTableCmd *cmd,                      LOCKMODE lockmode);static List *find_typed_table_dependencies(Oid typeOid,
constchar *typeName,                              DropBehavior behavior);static void ATPrepAddColumn(List **wqueue,
Relationrel, bool recurse, bool recursing,                AlterTableCmd *cmd, LOCKMODE lockmode);
 
-static void ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
+static void ATExecAddColumn(List **wqueue, List **affected,
+                AlteredTableInfo *tab, Relation rel,                ColumnDef *colDef, bool isOid,                bool
recurse,bool recursing, LOCKMODE lockmode);static void check_for_column_name_collision(Relation rel, const char
*colname);
@@ -341,13 +345,14 @@ static void ATExecSetStorage(Relation rel, const char *colName,                 Node *newValue,
LOCKMODElockmode);static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
 AlterTableCmd *cmd, LOCKMODE lockmode);
 
-static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
+static void ATExecDropColumn(List **wqueue, List **affected,
+                 Relation rel, const char *colName,                 DropBehavior behavior,                 bool
recurse,bool recursing,                 bool missing_ok, LOCKMODE lockmode);static void ATExecAddIndex(AlteredTableInfo
*tab,Relation rel,               IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
 
-static void ATExecAddConstraint(List **wqueue,
+static void ATExecAddConstraint(List **wqueue, List **affected,                    AlteredTableInfo *tab, Relation
rel,                   Constraint *newConstraint, bool recurse, bool is_readd,                    LOCKMODE lockmode);
 
@@ -467,10 +472,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)        ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),                errmsg("ON COMMIT can only be used on temporary tables")));
 
-    if (stmt->constraints != NIL && relkind == RELKIND_FOREIGN_TABLE)
-        ereport(ERROR,
-                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                 errmsg("constraints are not supported on foreign tables")));    /*     * Look up the namespace in
whichwe are supposed to create the relation,
 
@@ -2940,9 +2941,71 @@ AlterTableGetLockLevel(List *cmds)}static void
+PrintForeignNotice(List *affected, Relation rel)
+{
+    ListCell *l;
+    StringInfo namelist = NULL;
+    bool       rel_is_foreign = false;
+    int           state = 0;
+    int n = 0;
+
+    if (!affected) return;
+
+    foreach (l, affected)
+    {
+        Oid         relid = lfirst_oid(l);
+        Relation r = relation_open(relid, NoLock);
+        if (r->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+        {
+            if (relid == RelationGetRelid(rel))
+                rel_is_foreign = true;
+
+            switch (state)
+            {
+            case 0:
+                namelist = makeStringInfo();
+                appendStringInfoString(namelist, RelationGetRelationName(r));
+                state = 1;
+                break;
+
+            case 1:
+                if (namelist->len < 32)
+                {
+                    appendStringInfoString(namelist, ", ");
+                    appendStringInfoString(namelist,
+                                           RelationGetRelationName(r));
+                }
+                else
+                {
+                    appendStringInfoString(namelist, "...");
+                    state = 2;
+                }
+                break;
+
+            default:;
+                /* Do nothing */
+            }
+                
+            n++;
+        }
+        relation_close(r, NoLock);
+    }
+
+    /*
+     * Don't show this notice if the relation designated in this command is
+     * the only foreign table affected.
+     */
+    if (n > 1 || (n == 1 && !rel_is_foreign))
+        ereport(NOTICE,
+                (errmsg_plural("This command affects %d foreign table: %s",
+                               "This command affects %d foreign tables: %s",
+                               n, n, namelist->data)));
+}
+
+static voidATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode){
-    List       *wqueue = NIL;
+    List       *wqueue = NIL, *affected = NIL;    ListCell   *lcmd;    /* Phase 1: preliminary examination of
commands,create work queue */
 
@@ -2957,10 +3020,14 @@ ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)    relation_close(rel,
NoLock);   /* Phase 2: update system catalogs */
 
-    ATRewriteCatalogs(&wqueue, lockmode);
+    ATRewriteCatalogs(&wqueue, &affected, lockmode);    /* Phase 3: scan/rewrite tables as needed */
ATRewriteTables(&wqueue,lockmode);
 
+
+    /* Notice if foreign tables are affected by this command */
+    if (affected)
+        PrintForeignNotice(affected, rel);}/*
@@ -3019,24 +3086,28 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,             * rules.             */
         ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
 
-            ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+            /* 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);
 
-            ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+            /* 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);
 
-            ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+            /* 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*/
 
-            ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+            /* 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,7 +3120,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,            break;        case
AT_SetStorage:       /* ALTER COLUMN SET STORAGE */            ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
 
-            ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+            /* Don't recurse to child tables that are foreign */
+            ATSimpleRecursion(wqueue, rel, cmd, recurse, false, lockmode);            /* No command-specific prep
needed*/            pass = AT_PASS_MISC;            break;
 
@@ -3067,7 +3139,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,            pass = AT_PASS_ADD_INDEX;
        break;        case AT_AddConstraint:    /* ADD CONSTRAINT */
 
-            ATSimplePermissions(rel, ATT_TABLE);
+            ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);            /* Recursion occurs during execution
phase*/            /* No command-specific prep needed except saving recurse flag */            if (recurse)
 
@@ -3081,7 +3153,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,            pass = AT_PASS_ADD_CONSTR;
         break;        case AT_DropConstraint:    /* DROP CONSTRAINT */
 
-            ATSimplePermissions(rel, ATT_TABLE);
+            ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);            /* Recursion occurs during execution
phase*/            /* No command-specific prep needed except saving recurse flag */            if (recurse)
 
@@ -3149,13 +3221,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,            pass = AT_PASS_MISC;
     break;        case AT_AddInherit:        /* INHERIT */
 
-            ATSimplePermissions(rel, ATT_TABLE);
+            ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);            /* This command never recurses */
     ATPrepAddInherit(rel);            pass = AT_PASS_MISC;            break;
 
+        case AT_DropInherit:    /* NO INHERIT */
+            ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
+            /* This command never recurses */
+            /* No command-specific prep needed */
+            pass = AT_PASS_MISC;
+            break;        case AT_AlterConstraint:        /* ALTER CONSTRAINT */
-            ATSimplePermissions(rel, ATT_TABLE);
+            ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);            pass = AT_PASS_MISC;            break;
      case AT_ValidateConstraint:        /* VALIDATE CONSTRAINT */
 
@@ -3184,7 +3262,6 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,        case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:        case AT_DisableRule:
 
-        case AT_DropInherit:    /* NO INHERIT */        case AT_AddOf:            /* OF */        case AT_DropOf: /*
NOTOF */            ATSimplePermissions(rel, ATT_TABLE);
 
@@ -3217,7 +3294,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, * conflicts). */static void
-ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
+ATRewriteCatalogs(List **wqueue, List **affected, LOCKMODE lockmode){    int            pass;    ListCell   *ltab;
@@ -3248,7 +3325,8 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)            rel = relation_open(tab->relid,
NoLock);           foreach(lcmd, subcmds)
 
-                ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd), lockmode);
+                ATExecCmd(wqueue, affected, tab, rel,
+                          (AlterTableCmd *) lfirst(lcmd), lockmode);            /*             * After the ALTER TYPE
pass,do cleanup work (this is not done in
 
@@ -3277,19 +3355,21 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode) * ATExecCmd: dispatch a subcommand to
appropriateexecution routine */static void
 
-ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
+ATExecCmd(List **wqueue, List **affected, AlteredTableInfo *tab, Relation rel,          AlterTableCmd *cmd, LOCKMODE
lockmode){
+    ATAddAffectedRelid(affected, RelationGetRelid(rel));
+    switch (cmd->subtype)    {        case AT_AddColumn:        /* ADD COLUMN */        case AT_AddColumnToView:
/* add column via CREATE OR REPLACE                                         * VIEW */
 
-            ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
+            ATExecAddColumn(wqueue, affected, tab, rel, (ColumnDef *) cmd->def,                            false,
false,false, lockmode);            break;        case AT_AddColumnRecurse:
 
-            ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
+            ATExecAddColumn(wqueue, affected, tab, rel, (ColumnDef *) cmd->def,                            false,
true,false, lockmode);            break;        case AT_ColumnDefault:    /* ALTER COLUMN DEFAULT */
 
@@ -3314,11 +3394,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,            ATExecSetStorage(rel,
cmd->name,cmd->def, lockmode);            break;        case AT_DropColumn:        /* DROP COLUMN */
 
-            ATExecDropColumn(wqueue, rel, cmd->name,
+            ATExecDropColumn(wqueue, affected, rel, cmd->name,                     cmd->behavior, false, false,
cmd->missing_ok,lockmode);            break;        case AT_DropColumnRecurse:        /* DROP COLUMN with recursion */
 
-            ATExecDropColumn(wqueue, rel, cmd->name,
+            ATExecDropColumn(wqueue, affected, rel, cmd->name,                      cmd->behavior, true, false,
cmd->missing_ok,lockmode);            break;        case AT_AddIndex:        /* ADD INDEX */
 
@@ -3328,16 +3408,19 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,            ATExecAddIndex(tab,
rel,(IndexStmt *) cmd->def, true, lockmode);            break;        case AT_AddConstraint:    /* ADD CONSTRAINT */
 
-            ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
+            ATExecAddConstraint(wqueue, affected, tab, rel,
+                                (Constraint *) cmd->def,                                 false, false, lockmode);
     break;        case AT_AddConstraintRecurse:    /* ADD CONSTRAINT with recursion */
 
-            ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
+            ATExecAddConstraint(wqueue, affected, tab, rel,
+                                (Constraint *) cmd->def,                                true, false, lockmode);
   break;        case AT_ReAddConstraint:        /* Re-add pre-existing check                                         *
constraint*/
 
-            ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
+            ATExecAddConstraint(wqueue, affected,
+                                tab, rel, (Constraint *) cmd->def,                                false, true,
lockmode);           break;        case AT_AddIndexConstraint:        /* ADD CONSTRAINT USING INDEX */
 
@@ -3383,13 +3466,15 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,        case AT_AddOids:        /*
SETWITH OIDS */            /* Use the ADD COLUMN code, unless prep decided to do nothing */            if (cmd->def !=
NULL)
-                ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
+                ATExecAddColumn(wqueue, affected,
+                                tab, rel, (ColumnDef *) cmd->def,                                true, false, false,
lockmode);           break;        case AT_AddOidsRecurse:    /* SET WITH OIDS */            /* Use the ADD COLUMN
code,unless prep decided to do nothing */            if (cmd->def != NULL)
 
-                ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
+                ATExecAddColumn(wqueue, affected,
+                                tab, rel, (ColumnDef *) cmd->def,                                true, true, false,
lockmode);           break;        case AT_DropOids:        /* SET WITHOUT OIDS */
 
@@ -4009,6 +4094,23 @@ ATGetQueueEntry(List **wqueue, Relation rel)}/*
+ * ATAddAffectedRelid: add a relid to affected list.
+ */
+static void
+ATAddAffectedRelid(List **affected, Oid relid)
+{
+    ListCell   *l;
+
+    foreach(l, *affected)
+    {
+        if (lfirst_oid(l) == relid)
+            return;
+    }
+
+    *affected = lappend_oid(*affected, relid);
+}
+
+/* * ATSimplePermissions * * - Ensure that it is a relation (or possibly a view)
@@ -4125,7 +4227,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets) */static voidATSimpleRecursion(List
**wqueue,Relation rel,
 
-                  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
+                  AlterTableCmd *cmd, bool recurse,
+                  bool include_foreign, LOCKMODE lockmode){    /*     * Propagate to children if desired.  Non-table
relationsnever have
 
@@ -4153,8 +4256,12 @@ ATSimpleRecursion(List **wqueue, Relation rel,                continue;            /*
find_all_inheritorsalready got lock */            childrel = relation_open(childrelid, NoLock);
 
-            CheckTableNotInUse(childrel, "ALTER TABLE");
-            ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
+            if (childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE
+                || include_foreign)
+            {
+                CheckTableNotInUse(childrel, "ALTER TABLE");
+                ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
+            }            relation_close(childrel, NoLock);        }    }
@@ -4421,7 +4528,8 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,}static void
-ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
+ATExecAddColumn(List **wqueue, List **affected,
+                AlteredTableInfo *tab, Relation rel,                ColumnDef *colDef, bool isOid,                bool
recurse,bool recursing, LOCKMODE lockmode){
 
@@ -4442,9 +4550,11 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,    ListCell   *child;
AclResult   aclresult;
 
+    ATAddAffectedRelid(affected, myrelid);
+    /* At top level, permission check was done in ATPrepCmd, else do it */    if (recursing)
-        ATSimplePermissions(rel, ATT_TABLE);
+        ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);    attrdesc = heap_open(AttributeRelationId,
RowExclusiveLock);
@@ -4746,7 +4856,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,        childtab =
ATGetQueueEntry(wqueue,childrel);        /* Recurse to child */
 
-        ATExecAddColumn(wqueue, childtab, childrel,
+        ATExecAddColumn(wqueue, affected, childtab, childrel,                        colDef, isOid, recurse, true,
lockmode);       heap_close(childrel, NoLock);
 
@@ -4859,7 +4969,19 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOC
ATPrepAddColumn(wqueue,rel, recurse, false, cmd, lockmode);    if (recurse)
 
+    {
+        /*
+         * Don't allow to add an OID column to inheritance tree that contains
+         * foreign table(s)
+         */
+      if (contains_foreign(RelationGetRelid(rel), AccessShareLock))
+            ereport(ERROR,
+                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                     errmsg("cannot add OID column to inheritance tree \"%s\" because it contains foreign table(s)",
+                            RelationGetRelationName(rel))));
+        cmd->subtype = AT_AddOidsRecurse;
+    }}/*
@@ -5327,7 +5449,8 @@ ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,}static void
-ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
+ATExecDropColumn(List **wqueue, List **affected,
+                 Relation rel, const char *colName,                 DropBehavior behavior,                 bool
recurse,bool recursing,                 bool missing_ok, LOCKMODE lockmode)
 
@@ -5338,9 +5461,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,    List       *children;
ObjectAddressobject;
 
+    ATAddAffectedRelid(affected, RelationGetRelid(rel));
+    /* At top level, permission check was done in ATPrepCmd, else do it */    if (recursing)
-        ATSimplePermissions(rel, ATT_TABLE);
+        ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);    /*     * get the number of the attribute
@@ -5426,7 +5551,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,                if
(childatt->attinhcount== 1 && !childatt->attislocal)                {                    /* Time to delete this child
column,too */
 
-                    ATExecDropColumn(wqueue, childrel, colName,
+                    ATExecDropColumn(wqueue, affected, childrel, colName,
behavior,true, true,                                     false, lockmode);                }
 
@@ -5644,12 +5769,15 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, * ALTER TABLE ADD CONSTRAINT
*/staticvoid
 
-ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
+ATExecAddConstraint(List **wqueue, List **affected,
+                    AlteredTableInfo *tab, Relation rel,                    Constraint *newConstraint, bool recurse,
boolis_readd,                    LOCKMODE lockmode){    Assert(IsA(newConstraint, Constraint));
 
+    ATAddAffectedRelid(affected, RelationGetRelid(rel));
+    /*     * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes     * arriving here (see the
preprocessingdone in parse_utilcmd.c).  Use a
 
@@ -5732,7 +5860,14 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,    /* At top level,
permissioncheck was done in ATPrepCmd, else do it */    if (recursing)
 
-        ATSimplePermissions(rel, ATT_TABLE);
+        ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
+
+    /* Don't allow ADD constraint NOT VALID 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
todo the work, making sure it works on
 
@@ -5743,9 +5878,17 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,     * omitted from the
returnedlist, which is what we want: we do not need     * to do any validation work.  That can only happen at child
tables,    * though, since we disallow merging at the top level.
 
-     */
+     *
+     * When propagating a NOT VALID option to children that are foreign tables,
+     * we quietly ignore the option.  Note that this is safe because foreign
+     * tables don't have any children.
+     */
+    constr = copyObject(constr);
+    if (tab->relkind == RELKIND_FOREIGN_TABLE &&
+        constr->skip_validation && recursing)
+        constr->skip_validation = false;    newcons = AddRelationNewConstraints(rel, NIL,
-                                        list_make1(copyObject(constr)),
+                                        list_make1(constr),                                        recursing,
/*allow_merge */                                        !recursing,        /* is_local */
        is_readd);        /* is_internal */
 
@@ -7225,7 +7368,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,    /* At top level, permission check
wasdone in ATPrepCmd, else do it */    if (recursing)
 
-        ATSimplePermissions(rel, ATT_TABLE);
+        ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);    conrel = heap_open(ConstraintRelationId,
RowExclusiveLock);
@@ -7560,7 +7703,10 @@ ATPrepAlterColumnType(List **wqueue,     * alter would put them out of step.     */    if
(recurse)
-        ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
+    {
+        /* 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,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index ded1841..35b5dd2 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -98,6 +98,7 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,       BufferAccessStrategy bstrategy, bool
for_wraparound,bool isTopLevel){    const char *stmttype;
 
+    VacuumMode    vacmode;    volatile bool in_outer_xact,                use_own_xacts;    List       *relations;
@@ -146,6 +147,20 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
ALLOCSET_DEFAULT_MAXSIZE);   /*
 
+     * Identify vacuum mode.  If relid is not InvalidOid, the caller should be
+     * an autovacuum worker.  See the above comments.
+     */
+    if (relid != InvalidOid)
+        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,7 +263,7 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
PushActiveSnapshot(GetTransactionSnapshot());               }
 
-                analyze_rel(relid, vacstmt, vac_strategy);
+                analyze_rel(relid, vacstmt, vacmode, vac_strategy);                if (use_own_xacts)
{
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 52dcc72..238bba2 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1337,11 +1337,12 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)        /*         *
Buildan RTE for the child, and attach to query's rangetable list.         * We copy most fields of the parent's RTE,
butreplace relation OID,
 
-         * and set inh = false.  Also, set requiredPerms to zero since all
-         * required permissions checks are done on the original RTE.
+         * relkind and set inh = false.  Also, set requiredPerms to zero since
+         * all required permissions checks are done on the original RTE.         */        childrte = copyObject(rte);
      childrte->relid = childOID;
 
+        childrte->relkind = newrelation->rd_rel->relkind;        childrte->inh = false;        childrte->requiredPerms
=0;        parse->rtable = lappend(parse->rtable, childrte);
 
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index b79af7a..18ced04 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -2062,6 +2062,16 @@ reparameterize_path(PlannerInfo *root, Path *path,        case T_SubqueryScan:            return
create_subqueryscan_path(root,rel, path->pathkeys,                                            required_outer);
 
+        case T_ForeignScan:
+            {
+                ForeignPath *fpath = (ForeignPath*) path;
+                ForeignPath *newpath = makeNode(ForeignPath);
+                memcpy(newpath, fpath, sizeof(ForeignPath));
+                newpath->path.param_info =
+                    get_baserel_parampathinfo(root, rel, required_outer);
+                /* cost recalc omitted */
+                return (Path *)newpath;
+            }        default:            break;    }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e3060a4..b5e47f4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4209,32 +4209,32 @@ AlterForeignServerStmt: ALTER SERVER name foreign_server_version
alter_generic_oCreateForeignTableStmt:       CREATE FOREIGN TABLE qualified_name            '(' OptTableElementList
')'
-            SERVER name create_generic_options
+            OptInherit SERVER name create_generic_options                {                    CreateForeignTableStmt
*n= makeNode(CreateForeignTableStmt);                    $4->relpersistence = RELPERSISTENCE_PERMANENT;
  n->base.relation = $4;                    n->base.tableElts = $6;
 
-                    n->base.inhRelations = NIL;
+                    n->base.inhRelations = $8;                    n->base.if_not_exists = false;                    /*
FDW-specificdata */
 
-                    n->servername = $9;
-                    n->options = $10;
+                    n->servername = $10;
+                    n->options = $11;                    $$ = (Node *) n;                }        | CREATE FOREIGN
TABLEIF_P NOT EXISTS qualified_name            '(' OptTableElementList ')'
 
-            SERVER name create_generic_options
+            OptInherit SERVER name create_generic_options                {                    CreateForeignTableStmt
*n= makeNode(CreateForeignTableStmt);                    $7->relpersistence = RELPERSISTENCE_PERMANENT;
  n->base.relation = $7;                    n->base.tableElts = $9;
 
-                    n->base.inhRelations = NIL;
+                    n->base.inhRelations = $11;                    n->base.if_not_exists = true;                    /*
FDW-specificdata */
 
-                    n->servername = $12;
-                    n->options = $13;
+                    n->servername = $13;
+                    n->options = $14;                    $$ = (Node *) n;                }        ;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 1e071d7..955f27c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -515,12 +515,6 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)                break;
  case CONSTR_CHECK:
 
-                if (cxt->isforeign)
-                    ereport(ERROR,
-                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                    errmsg("constraints are not supported on foreign tables"),
-                             parser_errposition(cxt->pstate,
-                                                constraint->location)));                cxt->ckconstraints =
lappend(cxt->ckconstraints,constraint);                break;
 
@@ -529,7 +523,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)                if
(cxt->isforeign)                   ereport(ERROR,                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
-                    errmsg("constraints are not supported on foreign tables"),
+                    errmsg("primary key or unique constraints are not supported on foreign tables"),
         parser_errposition(cxt->pstate,                                                constraint->location)));
       if (constraint->keys == NIL)
 
@@ -546,7 +540,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)                if
(cxt->isforeign)                   ereport(ERROR,                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
-                    errmsg("constraints are not supported on foreign tables"),
+                    errmsg("foreign key constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate,                                               constraint->location)));
 
@@ -605,10 +599,14 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)static
voidtransformTableConstraint(CreateStmtContext*cxt, Constraint *constraint){
 
-    if (cxt->isforeign)
+    if (cxt->isforeign &&
+        (constraint->contype == CONSTR_PRIMARY ||
+         constraint->contype == CONSTR_UNIQUE ||
+         constraint->contype == CONSTR_EXCLUSION ||
+         constraint->contype == CONSTR_FOREIGN))        ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                 errmsg("constraints are not supported on foreign tables"),
+                 errmsg("primary key, unique, exclusion, or foreign key constraints are not supported on foreign
tables"),                parser_errposition(cxt->pstate,                                    constraint->location)));
 
diff --git a/src/include/catalog/pg_inherits_fn.h b/src/include/catalog/pg_inherits_fn.h
index 757d849..228573a 100644
--- a/src/include/catalog/pg_inherits_fn.h
+++ b/src/include/catalog/pg_inherits_fn.h
@@ -21,6 +21,7 @@ extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode);extern List
*find_all_inheritors(OidparentrelId, LOCKMODE lockmode,                    List **parents);extern bool has_subclass(Oid
relationId);
+extern bool contains_foreign(Oid parentrelId, LOCKMODE lockmode);extern bool typeInheritsFrom(Oid subclassTypeId, Oid
superclassTypeId);#endif  /* PG_INHERITS_FN_H */
 
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 058dc5f..8253a07 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -140,6 +140,15 @@ extern int    vacuum_multixact_freeze_min_age;extern int    vacuum_multixact_freeze_table_age;
+/* Possible modes for vacuum() */
+typedef enum
+{
+    VAC_MODE_ALL,                /* Vacuum/analyze all relations */
+    VAC_MODE_SINGLE,            /* Vacuum/analyze a specific relation */
+    VAC_MODE_AUTOVACUUM            /* Autovacuum worker */
+} VacuumMode;
+
+/* in commands/vacuum.c */extern void vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,       BufferAccessStrategy
bstrategy,bool for_wraparound, bool isTopLevel);
 
@@ -174,7 +183,7 @@ extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
BufferAccessStrategybstrategy);/* in commands/analyze.c */
 
-extern void analyze_rel(Oid relid, VacuumStmt *vacstmt,
+extern void analyze_rel(Oid relid, VacuumStmt *vacstmt, VacuumMode vacmode,            BufferAccessStrategy
bstrategy);externbool std_typanalyze(VacAttrStats *stats);extern double anl_random_fract(void);
 
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 60506e0..f15d2e1 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -750,16 +750,12 @@ CREATE TABLE use_ft1_column_type (x ft1);ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE
integer;   -- ERRORERROR:  cannot alter foreign table "ft1" because column "use_ft1_column_type.x" uses its row
typeDROPTABLE use_ft1_column_type;
 
-ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0); -- ERROR
-ERROR:  constraints are not supported on foreign tables
-LINE 1: ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c...
-                                    ^
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const;
           -- ERROR
 
-ERROR:  "ft1" is not a table
+ERROR:  constraint "no_const" of relation "ft1" does not existALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS
no_const;
-ERROR:  "ft1" is not a table
-ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
-ERROR:  "ft1" is not a table
+NOTICE:  constraint "no_const" of relation "ft1" does not exist, skipping
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c9_check;ALTER FOREIGN TABLE ft1 SET WITH OIDS;
--ERRORERROR:  "ft1" is not a tableALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;
 
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
index f819eb1..2aa5ffe 100644
--- a/src/test/regress/sql/foreign_data.sql
+++ b/src/test/regress/sql/foreign_data.sql
@@ -314,10 +314,10 @@ ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET STATISTICS -1;CREATE TABLE use_ft1_column_type (x
ft1);ALTERFOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer;    -- ERRORDROP TABLE use_ft1_column_type;
 
-ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0); -- ERROR
+ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0);ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const;
           -- ERRORALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const;
 
-ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check;
+ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c9_check;ALTER FOREIGN TABLE ft1 SET WITH OIDS;
--ERRORALTER FOREIGN TABLE ft1 OWNER TO regress_test_role;ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote
'~',ADD escape '@'); 

pgsql-hackers by date:

Previous
From: KONDO Mitsumasa
Date:
Subject: Re: gaussian distribution pgbench
Next
From: Etsuro Fujita
Date:
Subject: Re: inherit support for foreign tables