simplify_explain_routines.txt

text/plain

Filename: simplify_explain_routines.txt
Type: text/plain
Part: 0
Message: Re: explain plans for foreign servers
/*
 * 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);
}