Hi,

I spent more time reviewing this patch. Here are additional comments.

1/ Remove unnecessary includes

#include "commands/explain_state.h" from option.c.
#include "utils/json.h" from postgres_fdw.c.

2/ Add new EXPLAIN options

MEMORY and SUMMARY flags added to the remote EXPLAIN query formatting.
These do not require ANALYZE to be used.

A larger question is how would we want to ensure that new core EXPLAIN
options can be automatically set?

This is not quite common, but perhaps it is a good thing to add a comment
near ExplainState explaining that if a new option is added, make sure
that postgre_fdw remote_plans are updated.

```
typedef struct ExplainState
{
  StringInfo  str;      /* output buffer */
  /* options */
  bool    verbose;    /* be verbose */
  bool    analyze;    /* print actual times */
  bool    costs;      /* print estimated co

```

3/ Removed unnecessary pstrdup() when appending remote plan rows to
explain_plan.

Removed unnecessary pstrdup() when appending remote plan rows to explain_plan.

```
+                       appendStringInfo(&explain->explain_plan,
"%s\n", pstrdup(PQgetvalue(res, i, 0)));
```


4/ Simplify foreign table OID handling in postgresExplainForeignScan

I am not sure why we need a list that can only hold a single value.
Can we just use an Oid variable to store this?


5/ Encapsulates getting the connection, executing the remote EXPLAIN,
and releasing the connection.

Replaces repeated code in postgresExplainForeignScan,
postgresExplainForeignModify, and postgresExplainDirectModify.

For #4 and #5, attached is my attempt to simplify these routines. What
do you think?

6/ Updated typedefs.list

... to include PgFdwExplainRemotePlans and PgFdwExplainState.


7/ Tests

I quickly skimmed the tests, but I did not see a join push-down test.
We should add
that.


--
Sami Imseih
Amazon Web Services (AWS)
/*
 * 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);
}

Reply via email to