mirror of
https://github.com/postgres/postgres.git
synced 2025-06-11 20:28:21 +03:00
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
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user