pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Etsuro Fujita <fujita.etsuro@lab.ntt.co.jp>
To: Shigeru Hanada <shigeru.hanada@gmail.com>
Cc: PostgreSQL-development <pgsql-hackers@postgresql.org>
Subject: Re: [Review] inherit support for foreign tables
Date: Fri, 31 Jan 2014 11:55:40 +0900
Message-ID: <52EB10AC.4070307@lab.ntt.co.jp> (raw)
In-Reply-To: <52E7AA1A.7000008@lab.ntt.co.jp>
References: <52E11225.2080907@lab.ntt.co.jp>
	<CAEZqfEf_MO_pHJqMjr2i6Y7OVUrbJJB3cWHm=4qmef8mFK+V=Q@mail.gmail.com>
	<52E5D9AD.4000202@lab.ntt.co.jp>
	<CAEZqfEfLkbOnitJyNHQoX7VHH7-9=5hHpVyNABfE_pQXa6q-YQ@mail.gmail.com>
	<52E7AA1A.7000008@lab.ntt.co.jp>
List-Unsubscribe: <mailto:majordomo@postgresql.org?body=unsub%20pgsql-hackers>

(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-8n@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

Attachments:

  [text/plain] foreign_inherit-v1.1.patch (30.2K, ../52EB10AC.4070307@lab.ntt.co.jp/2-foreign_inherit-v1.1.patch)
  download | inline diff:
*** 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 '@');


view thread (179+ messages)  latest in thread

Message-ID: <52EB10AC.4070307@lab.ntt.co.jp>
Permalink:  ../52EB10AC.4070307@lab.ntt.co.jp/
Also on:    postgresql.org/message-id/52EB10AC.4070307@lab.ntt.co.jp

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: fujita.etsuro@lab.ntt.co.jp, shigeru.hanada@gmail.com
  Subject: Re: [Review] inherit support for foreign tables
  In-Reply-To: <52EB10AC.4070307@lab.ntt.co.jp>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox