mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
Allow foreign tables to participate in inheritance.
Foreign tables can now be inheritance children, or parents. Much of the system was already ready for this, but we had to fix a few things of course, mostly in the area of planner and executor handling of row locks. As side effects of this, allow foreign tables to have NOT VALID CHECK constraints (and hence to accept ALTER ... VALIDATE CONSTRAINT), and to accept ALTER SET STORAGE and ALTER SET WITH/WITHOUT OIDS. Continuing to disallow these things would've required bizarre and inconsistent special cases in inheritance behavior. Since foreign tables don't enforce CHECK constraints anyway, a NOT VALID one is a complete no-op, but that doesn't mean we shouldn't allow it. And it's possible that some FDWs might have use for SET STORAGE or SET WITH OIDS, though doubtless they will be no-ops for most. An additional change in support of this is that when a ModifyTable node has multiple target tables, they will all now be explicitly identified in EXPLAIN output, for example: Update on pt1 (cost=0.00..321.05 rows=3541 width=46) Update on pt1 Foreign Update on ft1 Foreign Update on ft2 Update on child3 -> Seq Scan on pt1 (cost=0.00..0.00 rows=1 width=46) -> Foreign Scan on ft1 (cost=100.00..148.03 rows=1170 width=46) -> Foreign Scan on ft2 (cost=100.00..148.03 rows=1170 width=46) -> Seq Scan on child3 (cost=0.00..25.00 rows=1200 width=46) This was done mainly to provide an unambiguous place to attach "Remote SQL" fields, but it is useful for inherited updates even when no foreign tables are involved. Shigeru Hanada and Etsuro Fujita, reviewed by Ashutosh Bapat and Kyotaro Horiguchi, some additional hacking by me
This commit is contained in:
@ -297,9 +297,8 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
|
||||
* do_analyze_rel() -- analyze one relation, recursively or not
|
||||
*
|
||||
* Note that "acquirefunc" is only relevant for the non-inherited case.
|
||||
* If we supported foreign tables in inheritance trees,
|
||||
* acquire_inherited_sample_rows would need to determine the appropriate
|
||||
* acquirefunc for each child table.
|
||||
* For the inherited case, acquire_inherited_sample_rows() determines the
|
||||
* appropriate acquirefunc for each child table.
|
||||
*/
|
||||
static void
|
||||
do_analyze_rel(Relation onerel, int options, List *va_cols,
|
||||
@ -1448,7 +1447,8 @@ compare_rows(const void *a, const void *b)
|
||||
*
|
||||
* This has the same API as acquire_sample_rows, except that rows are
|
||||
* collected from all inheritance children as well as the specified table.
|
||||
* We fail and return zero if there are no inheritance children.
|
||||
* We fail and return zero if there are no inheritance children, or if all
|
||||
* children are foreign tables that don't support ANALYZE.
|
||||
*/
|
||||
static int
|
||||
acquire_inherited_sample_rows(Relation onerel, int elevel,
|
||||
@ -1457,6 +1457,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
|
||||
{
|
||||
List *tableOIDs;
|
||||
Relation *rels;
|
||||
AcquireSampleRowsFunc *acquirefuncs;
|
||||
double *relblocks;
|
||||
double totalblocks;
|
||||
int numrows,
|
||||
@ -1491,10 +1492,12 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the blocks in all the relations. The result could overflow
|
||||
* BlockNumber, so we use double arithmetic.
|
||||
* Identify acquirefuncs to use, and count blocks in all the relations.
|
||||
* The result could overflow BlockNumber, so we use double arithmetic.
|
||||
*/
|
||||
rels = (Relation *) palloc(list_length(tableOIDs) * sizeof(Relation));
|
||||
acquirefuncs = (AcquireSampleRowsFunc *)
|
||||
palloc(list_length(tableOIDs) * sizeof(AcquireSampleRowsFunc));
|
||||
relblocks = (double *) palloc(list_length(tableOIDs) * sizeof(double));
|
||||
totalblocks = 0;
|
||||
nrels = 0;
|
||||
@ -1502,6 +1505,8 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
|
||||
{
|
||||
Oid childOID = lfirst_oid(lc);
|
||||
Relation childrel;
|
||||
AcquireSampleRowsFunc acquirefunc = NULL;
|
||||
BlockNumber relpages = 0;
|
||||
|
||||
/* We already got the needed lock */
|
||||
childrel = heap_open(childOID, NoLock);
|
||||
@ -1515,12 +1520,66 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check table type (MATVIEW can't happen, but might as well allow) */
|
||||
if (childrel->rd_rel->relkind == RELKIND_RELATION ||
|
||||
childrel->rd_rel->relkind == RELKIND_MATVIEW)
|
||||
{
|
||||
/* Regular table, so use the regular row acquisition function */
|
||||
acquirefunc = acquire_sample_rows;
|
||||
relpages = RelationGetNumberOfBlocks(childrel);
|
||||
}
|
||||
else if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
|
||||
{
|
||||
/*
|
||||
* For a foreign table, call the FDW's hook function to see
|
||||
* whether it supports analysis.
|
||||
*/
|
||||
FdwRoutine *fdwroutine;
|
||||
bool ok = false;
|
||||
|
||||
fdwroutine = GetFdwRoutineForRelation(childrel, false);
|
||||
|
||||
if (fdwroutine->AnalyzeForeignTable != NULL)
|
||||
ok = fdwroutine->AnalyzeForeignTable(childrel,
|
||||
&acquirefunc,
|
||||
&relpages);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
/* ignore, but release the lock on it */
|
||||
Assert(childrel != onerel);
|
||||
heap_close(childrel, AccessShareLock);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ignore, but release the lock on it */
|
||||
Assert(childrel != onerel);
|
||||
heap_close(childrel, AccessShareLock);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* OK, we'll process this child */
|
||||
rels[nrels] = childrel;
|
||||
relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel);
|
||||
totalblocks += relblocks[nrels];
|
||||
acquirefuncs[nrels] = acquirefunc;
|
||||
relblocks[nrels] = (double) relpages;
|
||||
totalblocks += (double) relpages;
|
||||
nrels++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we don't have at least two tables to consider, fail.
|
||||
*/
|
||||
if (nrels < 2)
|
||||
{
|
||||
ereport(elevel,
|
||||
(errmsg("skipping analyze of \"%s.%s\" inheritance tree --- this inheritance tree contains no analyzable child tables",
|
||||
get_namespace_name(RelationGetNamespace(onerel)),
|
||||
RelationGetRelationName(onerel))));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now sample rows from each relation, proportionally to its fraction of
|
||||
* the total block count. (This might be less than desirable if the child
|
||||
@ -1533,6 +1592,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
|
||||
for (i = 0; i < nrels; i++)
|
||||
{
|
||||
Relation childrel = rels[i];
|
||||
AcquireSampleRowsFunc acquirefunc = acquirefuncs[i];
|
||||
double childblocks = relblocks[i];
|
||||
|
||||
if (childblocks > 0)
|
||||
@ -1549,12 +1609,9 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
|
||||
tdrows;
|
||||
|
||||
/* Fetch a random sample of the child's rows */
|
||||
childrows = acquire_sample_rows(childrel,
|
||||
elevel,
|
||||
rows + numrows,
|
||||
childtargrows,
|
||||
&trows,
|
||||
&tdrows);
|
||||
childrows = (*acquirefunc) (childrel, elevel,
|
||||
rows + numrows, childtargrows,
|
||||
&trows, &tdrows);
|
||||
|
||||
/* We may need to convert from child's rowtype to parent's */
|
||||
if (childrows > 0 &&
|
||||
|
Reference in New Issue
Block a user