/* * explain_remote_query * Helper function to get connection and execute remote EXPLAIN */ static void explain_remote_query(int plan_node_id, ExplainState *es, PgFdwExplainState *pgfdw_explain_state, Oid foreign_table_oid, char *sql) { UserMapping *user; PGconn *conn; ForeignTable *table; table = GetForeignTable(foreign_table_oid); user = GetUserMapping(GetUserId(), table->serverid); conn = GetConnection(user, false, NULL); postgresExplainStatement(plan_node_id, es, pgfdw_explain_state, conn, sql); ReleaseConnection(conn); } /* * postgresExplainForeignScan * Produce extra output for EXPLAIN of a ForeignScan on a foreign table */ static void postgresExplainForeignScan(ForeignScanState *node, ExplainState *es) { ForeignScan *plan = castNode(ForeignScan, node->ss.ps.plan); List *fdw_private = plan->fdw_private; PgFdwExplainState *pgfdw_explain_state; char *sql; Oid foreign_table_oid = InvalidOid; if (node->ss.ss_currentRelation) foreign_table_oid = RelationGetRelid(node->ss.ss_currentRelation); /* * 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) { StringInfoData 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_base_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_base_relids, -1) - minrti; /* Now we can translate the string */ initStringInfo(&relations); ptr = rawrelations; while (*ptr) { if (isdigit((unsigned char) *ptr)) { int rti = strtol(ptr, &ptr, 10); RangeTblEntry *rte; char *relname; char *refname; rti += rtoffset; Assert(bms_is_member(rti, plan->fs_base_relids)); rte = rt_fetch(rti, es->rtable); Assert(rte->rtekind == RTE_RELATION); /* This logic should agree with explain.c's ExplainTargetRel */ relname = get_rel_name(rte->relid); /* * Save first table OID for getting server connection */ if (!OidIsValid(foreign_table_oid)) foreign_table_oid = rte->relid; if (es->verbose) { char *namespace; namespace = get_namespace_name_or_temp(get_rel_namespace(rte->relid)); appendStringInfo(&relations, "%s.%s", quote_identifier(namespace), quote_identifier(relname)); } else appendStringInfoString(&relations, 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); } sql = strVal(list_nth(fdw_private, FdwScanPrivateSelectSql)); /* * Add remote query, when VERBOSE option is specified. */ if (es->verbose) ExplainPropertyText("Remote SQL", sql, es); pgfdw_explain_state = GetExplainExtensionState(es, GET_EXTENSION_ID()); /* If we don't have a foreign table oid by now, something went wrong */ Assert(foreign_table_oid); if (pgfdw_explain_state && pgfdw_explain_state->remote_plans) explain_remote_query(node->ss.ps.plan->plan_node_id, es, pgfdw_explain_state, foreign_table_oid, sql); } /* * postgresExplainForeignModify * Produce extra output for EXPLAIN of a ModifyTable on a foreign table */ static void postgresExplainForeignModify(ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es) { char *sql = strVal(list_nth(fdw_private, FdwModifyPrivateUpdateSql)); PgFdwExplainState *pgfdw_explain_state; if (es->verbose) { ExplainPropertyText("Remote SQL", sql, es); /* * For INSERT we should always have batch size >= 1, but UPDATE and * DELETE don't support batching so don't show the property. */ if (rinfo->ri_BatchSize > 0) ExplainPropertyInteger("Batch Size", NULL, rinfo->ri_BatchSize, es); } pgfdw_explain_state = GetExplainExtensionState(es, GET_EXTENSION_ID()); if (pgfdw_explain_state && pgfdw_explain_state->remote_plans) explain_remote_query(mtstate->ps.plan->plan_node_id, es, pgfdw_explain_state, rinfo->ri_RelationDesc->rd_rel->oid, sql); } /* * postgresExplainDirectModify * Produce extra output for EXPLAIN of a ForeignScan that modifies a * foreign table directly */ static void postgresExplainDirectModify(ForeignScanState *node, ExplainState *es) { List *fdw_private; char *sql; PgFdwExplainState *pgfdw_explain_state; fdw_private = ((ForeignScan *) node->ss.ps.plan)->fdw_private; sql = strVal(list_nth(fdw_private, FdwDirectModifyPrivateUpdateSql)); if (es->verbose) ExplainPropertyText("Remote SQL", sql, es); pgfdw_explain_state = GetExplainExtensionState(es, GET_EXTENSION_ID()); if (pgfdw_explain_state && pgfdw_explain_state->remote_plans) explain_remote_query(node->ss.ps.plan->plan_node_id, es, pgfdw_explain_state, RelationGetRelid(node->ss.ss_currentRelation), sql); }