diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2019-12-02 16:31:03 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2019-12-02 16:31:03 -0500 |
commit | 4526951d564a7eed512b4a0ac3b5893e0a115690 (patch) | |
tree | fdf1c5cea4c021ffa178de3baf4aa4fd3a785b0b /contrib/postgres_fdw/postgres_fdw.c | |
parent | 1d468b9ad81b9139b4a0b16b416c3597925af4b0 (diff) | |
download | postgresql-4526951d564a7eed512b4a0ac3b5893e0a115690.tar.gz postgresql-4526951d564a7eed512b4a0ac3b5893e0a115690.zip |
Make postgres_fdw's "Relations" output agree with the rest of EXPLAIN.
The relation aliases shown in the "Relations" line for a foreign scan
didn't always agree with those used in the rest of EXPLAIN's output.
The regression test result changes appearing here provide examples.
It's really impossible for postgres_fdw to duplicate EXPLAIN's alias
assignment logic during postgresGetForeignRelSize(), because of the
de-duplication that EXPLAIN does on a global basis --- and anyway,
trying to duplicate that would be unmaintainable. Instead, just put
numeric rangetable indexes into the string, and convert those to
table names/aliases in postgresExplainForeignScan, which does have
access to the results of ruleutils.c's alias assignment logic.
Aside from being more reliable, this shifts some work from planning
to EXPLAIN, which is a good tradeoff for performance. (I also
changed from using StringInfo to using psprintf, which makes the
code slightly simpler and reduces its memory consumption.)
A kluge required by this solution is that we have to reverse-engineer
the rtoffset applied by setrefs.c. If that logic ever fails
(presumably because the member tables of a join got offset by
different amounts), we'll need some more cooperation with setrefs.c
to keep things straight. But for now, there's no need for that.
Arguably this is a back-patchable bug fix, but since this is a mostly
cosmetic issue and there have been no field complaints, I'll refrain
for now.
Discussion: https://postgr.es/m/12424.1575168015@sss.pgh.pa.us
Diffstat (limited to 'contrib/postgres_fdw/postgres_fdw.c')
-rw-r--r-- | contrib/postgres_fdw/postgres_fdw.c | 132 |
1 files changed, 95 insertions, 37 deletions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index fa142960eb5..3eb4e4044d4 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -12,6 +12,8 @@ */ #include "postgres.h" +#include <limits.h> + #include "access/htup_details.h" #include "access/sysattr.h" #include "access/table.h" @@ -574,9 +576,6 @@ postgresGetForeignRelSize(PlannerInfo *root, PgFdwRelationInfo *fpinfo; ListCell *lc; RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); - const char *namespace; - const char *relname; - const char *refname; /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -719,21 +718,11 @@ postgresGetForeignRelSize(PlannerInfo *root, } /* - * Set the name of relation in fpinfo, while we are constructing it here. - * It will be used to build the string describing the join relation in - * EXPLAIN output. We can't know whether VERBOSE option is specified or - * not, so always schema-qualify the foreign table name. + * fpinfo->relation_name gets the numeric rangetable index of the foreign + * table RTE. (If this query gets EXPLAIN'd, we'll convert that to a + * human-readable string at that time.) */ - fpinfo->relation_name = makeStringInfo(); - namespace = get_namespace_name(get_rel_namespace(foreigntableid)); - relname = get_rel_name(foreigntableid); - refname = rte->eref->aliasname; - appendStringInfo(fpinfo->relation_name, "%s.%s", - quote_identifier(namespace), - quote_identifier(relname)); - if (*refname && strcmp(refname, relname) != 0) - appendStringInfo(fpinfo->relation_name, " %s", - quote_identifier(rte->eref->aliasname)); + fpinfo->relation_name = psprintf("%u", baserel->relid); /* No outer and inner relations. */ fpinfo->make_outerrel_subquery = false; @@ -1376,7 +1365,7 @@ postgresGetForeignPlan(PlannerInfo *root, makeInteger(fpinfo->fetch_size)); if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel)) fdw_private = lappend(fdw_private, - makeString(fpinfo->relation_name->data)); + makeString(fpinfo->relation_name)); /* * Create the ForeignScan node for the given relation. @@ -2528,20 +2517,85 @@ postgresEndDirectModify(ForeignScanState *node) static void postgresExplainForeignScan(ForeignScanState *node, ExplainState *es) { - List *fdw_private; - char *sql; - char *relations; - - fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private; + ForeignScan *plan = castNode(ForeignScan, node->ss.ps.plan); + List *fdw_private = plan->fdw_private; /* - * Add names of relation handled by the foreign scan when the scan is a - * join + * Identify foreign scans that are really joins or upper relations. The + * input looks something like "(1) LEFT JOIN (2)", and we must replace the + * digit string(s), which are RT indexes, with the correct relation names. + * We do that here, not when the plan is created, because we can't know + * what aliases ruleutils.c will assign at plan creation time. */ if (list_length(fdw_private) > FdwScanPrivateRelations) { - relations = strVal(list_nth(fdw_private, FdwScanPrivateRelations)); - ExplainPropertyText("Relations", relations, es); + StringInfo relations; + char *rawrelations; + char *ptr; + int minrti, + rtoffset; + + rawrelations = strVal(list_nth(fdw_private, FdwScanPrivateRelations)); + + /* + * A difficulty with using a string representation of RT indexes is + * that setrefs.c won't update the string when flattening the + * rangetable. To find out what rtoffset was applied, identify the + * minimum RT index appearing in the string and compare it to the + * minimum member of plan->fs_relids. (We expect all the relids in + * the join will have been offset by the same amount; the Asserts + * below should catch it if that ever changes.) + */ + minrti = INT_MAX; + ptr = rawrelations; + while (*ptr) + { + if (isdigit((unsigned char) *ptr)) + { + int rti = strtol(ptr, &ptr, 10); + + if (rti < minrti) + minrti = rti; + } + else + ptr++; + } + rtoffset = bms_next_member(plan->fs_relids, -1) - minrti; + + /* Now we can translate the string */ + relations = makeStringInfo(); + ptr = rawrelations; + while (*ptr) + { + if (isdigit((unsigned char) *ptr)) + { + int rti = strtol(ptr, &ptr, 10); + RangeTblEntry *rte; + char *namespace; + char *relname; + char *refname; + + rti += rtoffset; + Assert(bms_is_member(rti, plan->fs_relids)); + rte = rt_fetch(rti, es->rtable); + Assert(rte->rtekind == RTE_RELATION); + /* This logic should agree with explain.c's ExplainTargetRel */ + namespace = get_namespace_name(get_rel_namespace(rte->relid)); + relname = get_rel_name(rte->relid); + appendStringInfo(relations, "%s.%s", + quote_identifier(namespace), + quote_identifier(relname)); + refname = (char *) list_nth(es->rtable_names, rti - 1); + if (refname == NULL) + refname = rte->eref->aliasname; + if (strcmp(refname, relname) != 0) + appendStringInfo(relations, " %s", + quote_identifier(refname)); + } + else + appendStringInfoChar(relations, *ptr++); + } + ExplainPropertyText("Relations", relations->data, es); } /* @@ -2549,6 +2603,8 @@ postgresExplainForeignScan(ForeignScanState *node, ExplainState *es) */ if (es->verbose) { + char *sql; + sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql)); ExplainPropertyText("Remote SQL", sql, es); } @@ -5171,13 +5227,14 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, /* * Set the string describing this join relation to be used in EXPLAIN - * output of corresponding ForeignScan. + * output of corresponding ForeignScan. Note that the decoration we add + * to the base relation names mustn't include any digits, or it'll confuse + * postgresExplainForeignScan. */ - fpinfo->relation_name = makeStringInfo(); - appendStringInfo(fpinfo->relation_name, "(%s) %s JOIN (%s)", - fpinfo_o->relation_name->data, - get_jointype_name(fpinfo->jointype), - fpinfo_i->relation_name->data); + fpinfo->relation_name = psprintf("(%s) %s JOIN (%s)", + fpinfo_o->relation_name, + get_jointype_name(fpinfo->jointype), + fpinfo_i->relation_name); /* * Set the relation index. This is defined as the position of this @@ -5723,11 +5780,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, /* * Set the string describing this grouped relation to be used in EXPLAIN - * output of corresponding ForeignScan. + * output of corresponding ForeignScan. Note that the decoration we add + * to the base relation name mustn't include any digits, or it'll confuse + * postgresExplainForeignScan. */ - fpinfo->relation_name = makeStringInfo(); - appendStringInfo(fpinfo->relation_name, "Aggregate on (%s)", - ofpinfo->relation_name->data); + fpinfo->relation_name = psprintf("Aggregate on (%s)", + ofpinfo->relation_name); return true; } |