diff options
Diffstat (limited to 'src/backend/commands/explain.c')
-rw-r--r-- | src/backend/commands/explain.c | 73 |
1 files changed, 59 insertions, 14 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 93b1f34ca0c..a14cae14420 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -15,6 +15,7 @@ #include "access/xact.h" #include "catalog/pg_type.h" +#include "commands/createas.h" #include "commands/defrem.h" #include "commands/prepare.h" #include "executor/hashjoin.h" @@ -45,7 +46,7 @@ explain_get_index_name_hook_type explain_get_index_name_hook = NULL; #define X_CLOSE_IMMEDIATE 2 #define X_NOWHITESPACE 4 -static void ExplainOneQuery(Query *query, ExplainState *es, +static void ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params); static void report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es); @@ -212,7 +213,8 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString, /* Explain every plan */ foreach(l, rewritten) { - ExplainOneQuery((Query *) lfirst(l), &es, queryString, params); + ExplainOneQuery((Query *) lfirst(l), NULL, &es, + queryString, params); /* Separate plans with an appropriate separator */ if (lnext(l) != NULL) @@ -288,21 +290,23 @@ ExplainResultDesc(ExplainStmt *stmt) /* * ExplainOneQuery - * print out the execution plan for one Query + * + * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt. */ static void -ExplainOneQuery(Query *query, ExplainState *es, +ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params) { /* planner will not cope with utility statements */ if (query->commandType == CMD_UTILITY) { - ExplainOneUtility(query->utilityStmt, es, queryString, params); + ExplainOneUtility(query->utilityStmt, into, es, queryString, params); return; } /* if an advisor plugin is present, let it manage things */ if (ExplainOneQuery_hook) - (*ExplainOneQuery_hook) (query, es, queryString, params); + (*ExplainOneQuery_hook) (query, into, es, queryString, params); else { PlannedStmt *plan; @@ -311,7 +315,7 @@ ExplainOneQuery(Query *query, ExplainState *es, plan = pg_plan_query(query, 0, params); /* run it (if needed) and produce output */ - ExplainOnePlan(plan, es, queryString, params); + ExplainOnePlan(plan, into, es, queryString, params); } } @@ -321,18 +325,36 @@ ExplainOneQuery(Query *query, ExplainState *es, * (In general, utility statements don't have plans, but there are some * we treat as special cases) * + * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt. + * * This is exported because it's called back from prepare.c in the - * EXPLAIN EXECUTE case + * EXPLAIN EXECUTE case. */ void -ExplainOneUtility(Node *utilityStmt, ExplainState *es, +ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params) { if (utilityStmt == NULL) return; - if (IsA(utilityStmt, ExecuteStmt)) - ExplainExecuteQuery((ExecuteStmt *) utilityStmt, es, + if (IsA(utilityStmt, CreateTableAsStmt)) + { + /* + * We have to rewrite the contained SELECT and then pass it back + * to ExplainOneQuery. It's probably not really necessary to copy + * the contained parsetree another time, but let's be safe. + */ + CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt; + List *rewritten; + + Assert(IsA(ctas->query, Query)); + rewritten = QueryRewrite((Query *) copyObject(ctas->query)); + Assert(list_length(rewritten) == 1); + ExplainOneQuery((Query *) linitial(rewritten), ctas->into, es, + queryString, params); + } + else if (IsA(utilityStmt, ExecuteStmt)) + ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es, queryString, params); else if (IsA(utilityStmt, NotifyStmt)) { @@ -356,6 +378,9 @@ ExplainOneUtility(Node *utilityStmt, ExplainState *es, * given a planned query, execute it if needed, and then print * EXPLAIN output * + * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt, + * in which case executing the query should result in creating that table. + * * Since we ignore any DeclareCursorStmt that might be attached to the query, * if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll actually run the * query. This is different from pre-8.3 behavior but seems more useful than @@ -366,9 +391,10 @@ ExplainOneUtility(Node *utilityStmt, ExplainState *es, * to call it. */ void -ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es, +ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params) { + DestReceiver *dest; QueryDesc *queryDesc; instr_time starttime; double totaltime = 0; @@ -392,16 +418,27 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es, PushCopiedSnapshot(GetActiveSnapshot()); UpdateActiveSnapshotCommandId(); - /* Create a QueryDesc requesting no output */ + /* + * Normally we discard the query's output, but if explaining CREATE TABLE + * AS, we'd better use the appropriate tuple receiver. + */ + if (into) + dest = CreateIntoRelDestReceiver(into); + else + dest = None_Receiver; + + /* Create a QueryDesc for the query */ queryDesc = CreateQueryDesc(plannedstmt, queryString, GetActiveSnapshot(), InvalidSnapshot, - None_Receiver, params, instrument_option); + dest, params, instrument_option); /* Select execution options */ if (es->analyze) eflags = 0; /* default run-to-completion flags */ else eflags = EXEC_FLAG_EXPLAIN_ONLY; + if (into) + eflags |= GetIntoRelEFlags(into); /* call ExecutorStart to prepare the plan for execution */ ExecutorStart(queryDesc, eflags); @@ -409,8 +446,16 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es, /* Execute the plan for statistics if asked for */ if (es->analyze) { + ScanDirection dir; + + /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */ + if (into && into->skipData) + dir = NoMovementScanDirection; + else + dir = ForwardScanDirection; + /* run the plan */ - ExecutorRun(queryDesc, ForwardScanDirection, 0L); + ExecutorRun(queryDesc, dir, 0L); /* run cleanup too */ ExecutorFinish(queryDesc); |