(2014/01/28 22:01), Etsuro Fujita wrote:
(2014/01/27 21:49), Shigeru Hanada wrote:
Is it too big change that making ANALYZE command to handle foreign
tables too even if no table name was specified?  IIRC, performance
issue was the reason to exclude foreign tables from auto-analyze
processing.  ANALYZEing large database contains local huge data also
takes long time.  One idea to avoid unexpected long processing is to
add option to foreign tables to mark it as "not-auto-analyzable".

Maybe I didn't express my idea clearly.  Sorry for that.

I don't think that we now allow the ANALYZE command to handle foreign
tables when no table name is specified with the command.  I think that
we allow the ANALYZE command to handle an inheritance tree that includes
foreign tables when the name of the parent table is specified, without
ignoring such foreign tables in the caluculation.  ISTM it would be
possible to do so if we introduce a new parameter, say, vac_mode, which
indicates wether vacuum() is called with a specific table or not.

I'll try to modify the ANALYZE command to do so on top of you patch.

Done on top of your patch, foreign_inherit.patch, not the latest version, foreign_inherit-v2.patch. As I proposed, an inheritance tree that includes foreign tables is now ANALYZEd, without ignoring such foreign tables in the inherited-stats computation, if the name of the parent table is specified with the ANALYZE command. (That has been done by a small modification of analyze.c, thanks to [1].) The ANALYZE command with no tablename or the autovacuum worker skips the inherited-stats computation itself for inheritance trees that includes foreign tables, which is different from the original patch. To distinguish the ANALYZE-with-a-tablename command from the others (ie, the ANALYZE-with-no-tablename command or the autovacuum worker), I've introduced a new parameter, vacmode, in vacuum(), and thus called analyze_rel() with that parameter. Attached is the modified patch. Could you review the patch?

Thanks,

[1] http://www.postgresql.org/message-id/e1sgfoo-0006zf...@gemulon.postgresql.org

Best regards,
Etsuro Fujita
*** 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>
  
***************
*** 2021,2028 **** 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,
--- 2032,2039 ----
    </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.
+      </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,342 ----
         </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.
+        </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,176 ----
     </varlistentry>
  
     <varlistentry>
+     <term><replaceable class="PARAMETER">parent_table</replaceable></term>
+     <listitem>
+      <para>
+       The name of an existing table or foreign table from which the new 
foreign
+       table automatically inherits all columns, see
+       <xref linkend="ddl-inherit"> for 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
***************
*** 91,96 **** static void BlockSampler_Init(BlockSampler bs, BlockNumber 
nblocks,
--- 91,98 ----
                                  int samplesize);
  static bool BlockSampler_HasMore(BlockSampler bs);
  static BlockNumber BlockSampler_Next(BlockSampler bs);
+ static bool inheritance_tree_include_foreign(Relation onerel,
+                                                                               
         bool *isAnalyzable);
  static void compute_index_stats(Relation onerel, double totalrows,
                                        AnlIndexData *indexdata, int nindexes,
                                        HeapTuple *rows, int numrows,
***************
*** 114,120 **** 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;
--- 116,123 ----
   *    analyze_rel() -- analyze one relation
   */
  void
! analyze_rel(Oid relid, VacuumStmt *vacstmt, VacuumMode vacmode,
!                       BufferAccessStrategy bstrategy)
  {
        Relation        onerel;
        int                     elevel;
***************
*** 270,276 **** analyze_rel(Oid relid, VacuumStmt *vacstmt, 
BufferAccessStrategy bstrategy)
         * If there are child tables, do recursive ANALYZE.
         */
        if (onerel->rd_rel->relhassubclass)
!               do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, true, 
elevel);
  
        /*
         * Close source relation now, but keep lock so that no one deletes it
--- 273,288 ----
         * If there are child tables, do recursive ANALYZE.
         */
        if (onerel->rd_rel->relhassubclass)
!       {
!               bool            include_foreign;
!               bool            isAnalyzable;
! 
!               include_foreign = inheritance_tree_include_foreign(onerel,
!                                                                               
                                   &isAnalyzable);
! 
!               if (!include_foreign || (isAnalyzable && vacmode == 
VAC_MODE_SINGLE))
!                       do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, 
true, elevel);
!       }
  
        /*
         * Close source relation now, but keep lock so that no one deletes it
***************
*** 668,673 **** do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
--- 680,772 ----
  }
  
  /*
+  * Check wether the inheritance tree includes foreign tables.  And if so,
+  * check wether such foreign tables are all analyzable.
+  */
+ static bool
+ inheritance_tree_include_foreign(Relation onerel, bool *isAnalyzable)
+ {
+       bool            result = false;
+       List       *tableOIDs;
+       ListCell   *lc;
+ 
+       *isAnalyzable = true;
+ 
+       /*
+        * Find all members of inheritance set.  We only need AccessShareLock on
+        * the children.
+        */
+       tableOIDs =
+               find_all_inheritors(RelationGetRelid(onerel), AccessShareLock, 
NULL);
+ 
+       /*
+        * Check that there's at least one descendant, else fail.  This could
+        * happen despite analyze_rel's relhassubclass check, if table once had 
a
+        * child but no longer does.  In that case, we can clear the
+        * relhassubclass field so as not to make the same mistake again later.
+        * (This is safe because we hold ShareUpdateExclusiveLock.)
+        */
+       if (list_length(tableOIDs) < 2)
+       {
+               /* CCI because we already updated the pg_class row in this 
command */
+               CommandCounterIncrement();
+               SetRelationHasSubclass(RelationGetRelid(onerel), false);
+               return result;
+       }
+ 
+       /*
+        * Check wether the inheritance tree includes some foreign tables.
+        */
+       foreach(lc, tableOIDs)
+       {
+               Oid                     childOID = lfirst_oid(lc);
+               Relation        childrel;
+ 
+               /* Parent table should not be foreign */
+               if (childOID == RelationGetRelid(onerel))
+                       continue;
+ 
+               /* We already got the needed lock */
+               childrel = heap_open(childOID, NoLock);
+ 
+               /* Ignore if temp table of another backend */
+               if (RELATION_IS_OTHER_TEMP(childrel))
+               {
+                       /* ... but release the lock on it */
+                       Assert(childrel != onerel);
+                       heap_close(childrel, AccessShareLock);
+                       continue;
+               }
+ 
+               if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+               {
+                       FdwRoutine *fdwroutine;
+                       AcquireSampleRowsFunc func = NULL;
+                       BlockNumber relpages = 0;
+                       bool            ok = false;
+ 
+                       /* Found it */
+                       result = true;
+ 
+                       /* Check whether the FDW supports analysis */
+                       fdwroutine = GetFdwRoutineForRelation(childrel, false);
+ 
+                       if (fdwroutine->AnalyzeForeignTable != NULL)
+                               ok = fdwroutine->AnalyzeForeignTable(childrel,
+                                                                               
                         &func,
+                                                                               
                         &relpages);
+ 
+                       if (!ok)
+                               *isAnalyzable = false;
+               }
+ 
+               heap_close(childrel, AccessShareLock);
+       }
+ 
+       return result;
+ }
+ 
+ /*
   * Compute statistics about indexes of a relation
   */
  static void
***************
*** 1451,1456 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
--- 1550,1556 ----
  {
        List       *tableOIDs;
        Relation   *rels;
+       AcquireSampleRowsFunc *acquirefunc;
        double     *relblocks;
        double          totalblocks;
        int                     numrows,
***************
*** 1485,1490 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
--- 1585,1592 ----
         * 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;
***************
*** 1506,1512 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
                }
  
                rels[nrels] = childrel;
!               relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel);
                totalblocks += relblocks[nrels];
                nrels++;
        }
--- 1608,1638 ----
                }
  
                rels[nrels] = childrel;
! 
!               if (childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
!               {
!                       acquirefunc[nrels] = acquire_sample_rows;
!                       relblocks[nrels] = (double) 
RelationGetNumberOfBlocks(childrel);
!               }
!               else
!               {
!                       FdwRoutine *fdwroutine;
!                       BlockNumber relpages = 0;
!                       bool            ok = false;
! 
!                       acquirefunc[nrels] = NULL;
! 
!                       fdwroutine = GetFdwRoutineForRelation(childrel, false);
!                       Assert(fdwroutine->AnalyzeForeignTable != NULL)
! 
!                       ok = fdwroutine->AnalyzeForeignTable(childrel,
!                                                                               
                 &acquirefunc[nrels],
!                                                                               
                 &relpages);
!                       Assert(ok);
! 
!                       relblocks[nrels] = (double) relpages;
!               }
! 
                totalblocks += relblocks[nrels];
                nrels++;
        }
***************
*** 1524,1529 **** acquire_inherited_sample_rows(Relation onerel, int elevel,
--- 1650,1656 ----
        {
                Relation        childrel = rels[i];
                double          childblocks = relblocks[i];
+               AcquireSampleRowsFunc childacquirefunc = acquirefunc[i];
  
                if (childblocks > 0)
                {
***************
*** 1539,1550 **** 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 &&
--- 1666,1677 ----
                                                        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
***************
*** 465,474 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                 errmsg("ON COMMIT can only be used on 
temporary tables")));
!       if (stmt->constraints != NIL && relkind == RELKIND_FOREIGN_TABLE)
!               ereport(ERROR,
!                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
!                                errmsg("constraints are not supported on 
foreign tables")));
  
        /*
         * Look up the namespace in which we are supposed to create the 
relation,
--- 465,489 ----
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                 errmsg("ON COMMIT can only be used on 
temporary tables")));
! /*
!  * Shouldn't this have been checked in parser?
!  */
!       if (relkind == RELKIND_FOREIGN_TABLE)
!       {
!               ListCell   *lc;
!               foreach(lc, stmt->constraints)
!               {
!                       NewConstraint      *nc = lfirst(lc);
! 
!                       if (nc->contype != CONSTR_CHECK &&
!                               nc->contype != CONSTR_DEFAULT &&
!                               nc->contype != CONSTR_NULL &&
!                               nc->contype != CONSTR_NOTNULL)
!                               ereport(ERROR,
!                                               
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
!                                                errmsg("only check constraints 
are supported on foreign tables")));
!               }
!       }
  
        /*
         * Look up the namespace in which we are supposed to create the 
relation,
***************
*** 3044,3050 **** 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)
--- 3059,3065 ----
                        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)
***************
*** 3058,3064 **** 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)
--- 3073,3079 ----
                        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)
***************
*** 3126,3138 **** 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 
*/
--- 3141,3153 ----
                        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_AlterConstraint:                /* ALTER CONSTRAINT */
!                       ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
                        pass = AT_PASS_MISC;
                        break;
                case AT_ValidateConstraint:             /* VALIDATE CONSTRAINT 
*/
***************
*** 3161,3167 **** 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);
--- 3176,3181 ----
***************
*** 3169,3174 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
--- 3183,3194 ----
                        /* No command-specific prep needed */
                        pass = AT_PASS_MISC;
                        break;
+               case AT_DropInherit:    /* NO INHERIT */
+                       ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
+                       /* These commands never recurse */
+                       /* No command-specific prep needed */
+                       pass = AT_PASS_MISC;
+                       break;
                case AT_GenericOptions:
                        ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
                        /* No command-specific prep needed */
***************
*** 4421,4427 **** 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);
  
--- 4441,4447 ----
  
        /* 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);
  
***************
*** 5317,5323 **** 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
--- 5337,5343 ----
  
        /* 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
***************
*** 5708,5714 **** 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
--- 5728,5734 ----
  
        /* At top level, permission check was done in ATPrepCmd, else do it */
        if (recursing)
!               ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
  
        /*
         * Call AddRelationNewConstraints to do the work, making sure it works 
on
***************
*** 7197,7203 **** 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);
  
--- 7217,7223 ----
  
        /* 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);
  
*** a/src/backend/commands/vacuum.c
--- b/src/backend/commands/vacuum.c
***************
*** 96,101 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
--- 96,102 ----
           BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel)
  {
        const char *stmttype;
+       VacuumMode      vacmode;
        volatile bool in_outer_xact,
                                use_own_xacts;
        List       *relations;
***************
*** 144,149 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
--- 145,164 ----
                                                                                
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.
         */
***************
*** 246,252 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
                                        
PushActiveSnapshot(GetTransactionSnapshot());
                                }
  
!                               analyze_rel(relid, vacstmt, vac_strategy);
  
                                if (use_own_xacts)
                                {
--- 261,267 ----
                                        
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/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 4206,4237 **** 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;
                                }
                ;
--- 4206,4237 ----
  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 ----
***************
*** 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,608 ----
  static void
  transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
  {
!       if (cxt->isforeign && constraint->contype != CONSTR_CHECK)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                                errmsg("only check constraints are supported 
on foreign tables"),
                                 parser_errposition(cxt->pstate,
                                                                        
constraint->location)));
  
*** a/src/include/commands/vacuum.h
--- b/src/include/commands/vacuum.h
***************
*** 138,143 **** extern int     vacuum_freeze_min_age;
--- 138,152 ----
  extern int    vacuum_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);
***************
*** 170,176 **** 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);
--- 179,185 ----
                                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/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