(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