diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2015-03-22 13:53:11 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2015-03-22 13:53:21 -0400 |
commit | cb1ca4d800621dcae67ca6c799006de99fa4f0a5 (patch) | |
tree | 6f69242bd93e64f1529e846433c7d92dc1a2a59d /src/backend/commands/explain.c | |
parent | 8ac356cde312693aa79f6b2fe7c46b8ed6108787 (diff) | |
download | postgresql-cb1ca4d800621dcae67ca6c799006de99fa4f0a5.tar.gz postgresql-cb1ca4d800621dcae67ca6c799006de99fa4f0a5.zip |
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
Diffstat (limited to 'src/backend/commands/explain.c')
-rw-r--r-- | src/backend/commands/explain.c | 114 |
1 files changed, 97 insertions, 17 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index a951c55ed34..315a52849c9 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -2187,6 +2187,10 @@ ExplainScanTarget(Scan *plan, ExplainState *es) /* * Show the target of a ModifyTable node + * + * Here we show the nominal target (ie, the relation that was named in the + * original query). If the actual target(s) is/are different, we'll show them + * in show_modifytable_info(). */ static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es) @@ -2303,30 +2307,106 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es) /* * Show extra information for a ModifyTable node + * + * We have two objectives here. First, if there's more than one target table + * or it's different from the nominal target, identify the actual target(s). + * Second, give FDWs a chance to display extra info about foreign targets. */ static void show_modifytable_info(ModifyTableState *mtstate, ExplainState *es) { - FdwRoutine *fdwroutine = mtstate->resultRelInfo->ri_FdwRoutine; + ModifyTable *node = (ModifyTable *) mtstate->ps.plan; + const char *operation; + const char *foperation; + bool labeltargets; + int j; - /* - * If the first target relation is a foreign table, call its FDW to - * display whatever additional fields it wants to. For now, we ignore the - * possibility of other targets being foreign tables, although the API for - * ExplainForeignModify is designed to allow them to be processed. - */ - if (fdwroutine != NULL && - fdwroutine->ExplainForeignModify != NULL) + switch (node->operation) { - ModifyTable *node = (ModifyTable *) mtstate->ps.plan; - List *fdw_private = (List *) linitial(node->fdwPrivLists); - - fdwroutine->ExplainForeignModify(mtstate, - mtstate->resultRelInfo, - fdw_private, - 0, - es); + case CMD_INSERT: + operation = "Insert"; + foperation = "Foreign Insert"; + break; + case CMD_UPDATE: + operation = "Update"; + foperation = "Foreign Update"; + break; + case CMD_DELETE: + operation = "Delete"; + foperation = "Foreign Delete"; + break; + default: + operation = "???"; + foperation = "Foreign ???"; + break; + } + + /* Should we explicitly label target relations? */ + labeltargets = (mtstate->mt_nplans > 1 || + (mtstate->mt_nplans == 1 && + mtstate->resultRelInfo->ri_RangeTableIndex != node->nominalRelation)); + + if (labeltargets) + ExplainOpenGroup("Target Tables", "Target Tables", false, es); + + for (j = 0; j < mtstate->mt_nplans; j++) + { + ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j; + FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine; + + if (labeltargets) + { + /* Open a group for this target */ + ExplainOpenGroup("Target Table", NULL, true, es); + + /* + * In text mode, decorate each target with operation type, so that + * ExplainTargetRel's output of " on foo" will read nicely. + */ + if (es->format == EXPLAIN_FORMAT_TEXT) + { + appendStringInfoSpaces(es->str, es->indent * 2); + appendStringInfoString(es->str, + fdwroutine ? foperation : operation); + } + + /* Identify target */ + ExplainTargetRel((Plan *) node, + resultRelInfo->ri_RangeTableIndex, + es); + + if (es->format == EXPLAIN_FORMAT_TEXT) + { + appendStringInfoChar(es->str, '\n'); + es->indent++; + } + } + + /* Give FDW a chance */ + if (fdwroutine && fdwroutine->ExplainForeignModify != NULL) + { + List *fdw_private = (List *) list_nth(node->fdwPrivLists, j); + + fdwroutine->ExplainForeignModify(mtstate, + resultRelInfo, + fdw_private, + j, + es); + } + + if (labeltargets) + { + /* Undo the indentation we added in text format */ + if (es->format == EXPLAIN_FORMAT_TEXT) + es->indent--; + + /* Close the group */ + ExplainCloseGroup("Target Table", NULL, true, es); + } } + + if (labeltargets) + ExplainCloseGroup("Target Tables", "Target Tables", false, es); } /* |