diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c index b33b2211ca09f2fd67d2e793e95db3897c3196bd..d5fa6ac2d3136e788523cda8e45846f236a37e79 100644 --- a/contrib/auto_explain/auto_explain.c +++ b/contrib/auto_explain/auto_explain.c @@ -301,6 +301,7 @@ explain_ExecutorEnd(QueryDesc *queryDesc) es.verbose = auto_explain_log_verbose; es.buffers = (es.analyze && auto_explain_log_buffers); es.timing = (es.analyze && auto_explain_log_timing); + es.summary = es.analyze; es.format = auto_explain_log_format; ExplainBeginOutput(&es); diff --git a/doc/src/sgml/perform.sgml b/doc/src/sgml/perform.sgml index 94c11adaa4900bf466a3f3cba3c7ab12c03c65aa..5a087fbe6a098f8bfb2ac55c99cf1e767a486409 100644 --- a/doc/src/sgml/perform.sgml +++ b/doc/src/sgml/perform.sgml @@ -89,7 +89,6 @@ EXPLAIN SELECT * FROM tenk1; QUERY PLAN ------------------------------------------------------------- Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244) - Planning time: 0.113 ms </screen> </para> @@ -162,12 +161,6 @@ EXPLAIN SELECT * FROM tenk1; actually returned, updated, or deleted by the query. </para> - <para> - The <literal>Planning time</literal> shown is the time it took to generate - the query plan from the parsed query and optimize it. It does not include - rewriting and parsing. - </para> - <para> Returning to our example: @@ -177,7 +170,6 @@ EXPLAIN SELECT * FROM tenk1; QUERY PLAN ------------------------------------------------------------- Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244) - Planning time: 0.113 ms </screen> </para> @@ -206,7 +198,6 @@ EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 7000; ------------------------------------------------------------ Seq Scan on tenk1 (cost=0.00..483.00 rows=7001 width=244) Filter: (unique1 < 7000) - Planning time: 0.104 ms </screen> Notice that the <command>EXPLAIN</> output shows the <literal>WHERE</> @@ -243,7 +234,6 @@ EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100; Recheck Cond: (unique1 < 100) -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) Index Cond: (unique1 < 100) - Planning time: 0.093 ms </screen> Here the planner has decided to use a two-step plan: the child plan @@ -272,7 +262,6 @@ EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND stringu1 = 'xxx'; Filter: (stringu1 = 'xxx'::name) -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) Index Cond: (unique1 < 100) - Planning time: 0.089 ms </screen> The added condition <literal>stringu1 = 'xxx'</literal> reduces the @@ -294,7 +283,6 @@ EXPLAIN SELECT * FROM tenk1 WHERE unique1 = 42; ----------------------------------------------------------------------------- Index Scan using tenk1_unique1 on tenk1 (cost=0.29..8.30 rows=1 width=244) Index Cond: (unique1 = 42) - Planning time: 0.076 ms </screen> In this type of plan the table rows are fetched in index order, which @@ -323,7 +311,6 @@ EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000; Index Cond: (unique1 < 100) -> Bitmap Index Scan on tenk1_unique2 (cost=0.00..19.78 rows=999 width=0) Index Cond: (unique2 > 9000) - Planning time: 0.094 ms </screen> But this requires visiting both indexes, so it's not necessarily a win @@ -344,7 +331,6 @@ EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2 -> Index Scan using tenk1_unique2 on tenk1 (cost=0.29..71.27 rows=10 width=244) Index Cond: (unique2 > 9000) Filter: (unique1 < 100) - Planning time: 0.087 ms </screen> </para> @@ -378,7 +364,6 @@ WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2; Index Cond: (unique1 < 10) -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.91 rows=1 width=244) Index Cond: (unique2 = t1.unique2) - Planning time: 0.117 ms </screen> </para> @@ -430,7 +415,6 @@ WHERE t1.unique1 < 10 AND t2.unique2 < 10 AND t1.hundred < t2.hundred; -> Materialize (cost=0.29..8.51 rows=10 width=244) -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..8.46 rows=10 width=244) Index Cond: (unique2 < 10) - Planning time: 0.119 ms </screen> The condition <literal>t1.hundred < t2.hundred</literal> can't be @@ -478,7 +462,6 @@ WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; Recheck Cond: (unique1 < 100) -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) Index Cond: (unique1 < 100) - Planning time: 0.182 ms </screen> </para> @@ -509,7 +492,6 @@ WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; -> Sort (cost=197.83..200.33 rows=1000 width=244) Sort Key: t2.unique2 -> Seq Scan on onek t2 (cost=0.00..148.00 rows=1000 width=244) - Planning time: 0.195 ms </screen> </para> @@ -546,7 +528,6 @@ WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2; -> Index Scan using tenk1_unique2 on tenk1 t1 (cost=0.29..656.28 rows=101 width=244) Filter: (unique1 < 100) -> Index Scan using onek_unique2 on onek t2 (cost=0.28..224.79 rows=1000 width=244) - Planning time: 0.176 ms </screen> which shows that the planner thinks that sorting <literal>onek</> by @@ -780,6 +761,12 @@ ROLLBACK; decisions. </para> + <para> + The <literal>Planning time</literal> shown by <command>EXPLAIN + ANALYZE</command> is the time it took to generate the query plan from the + parsed query and optimize it. It does not include parsing or rewriting. + </para> + <para> The <literal>Execution time</literal> shown by <command>EXPLAIN ANALYZE</command> includes executor start-up and shut-down time, as well diff --git a/doc/src/sgml/ref/explain.sgml b/doc/src/sgml/ref/explain.sgml index 72776a0fdef92c8318cb8353c1f96bb0d81bba56..f14a58dfc6353a55e705e9dcfccd52ba8d5ed0a0 100644 --- a/doc/src/sgml/ref/explain.sgml +++ b/doc/src/sgml/ref/explain.sgml @@ -145,8 +145,8 @@ ROLLBACK; <para> Include information on the estimated startup and total cost of each plan node, as well as the estimated number of rows and the estimated - width of each row. Also, include the time spent planning the query, - if available. This parameter defaults to <literal>TRUE</literal>. + width of each row. + This parameter defaults to <literal>TRUE</literal>. </para> </listitem> </varlistentry> @@ -291,8 +291,7 @@ EXPLAIN SELECT * FROM foo; QUERY PLAN --------------------------------------------------------- Seq Scan on foo (cost=0.00..155.00 rows=10000 width=4) - Planning time: 0.114 ms -(2 rows) +(1 row) </programlisting> </para> @@ -312,8 +311,7 @@ EXPLAIN (FORMAT JSON) SELECT * FROM foo; "Total Cost": 155.00, + "Plan Rows": 10000, + "Plan Width": 4 + - }. + - "Planning Time": 0.114 + + } + } + ] (1 row) @@ -332,8 +330,7 @@ EXPLAIN SELECT * FROM foo WHERE i = 4; -------------------------------------------------------------- Index Scan using fi on foo (cost=0.00..5.98 rows=1 width=4) Index Cond: (i = 4) - Planning time: 0.073 ms -(3 rows) +(2 rows) </programlisting> </para> @@ -353,8 +350,7 @@ EXPLAIN (FORMAT YAML) SELECT * FROM foo WHERE i='4'; Total Cost: 5.98 + Plan Rows: 1 + Plan Width: 4 + - Index Cond: "(i = 4)" + - Planning Time: 0.073 + Index Cond: "(i = 4)" (1 row) </programlisting> @@ -386,7 +382,6 @@ EXPLAIN SELECT sum(i) FROM foo WHERE i < 10; Aggregate (cost=23.93..23.93 rows=1 width=4) -> Index Scan using fi on foo (cost=0.00..23.92 rows=6 width=4) Index Cond: (i < 10) - Planning time: 0.088 ms (3 rows) </programlisting> </para> @@ -410,7 +405,7 @@ EXPLAIN ANALYZE EXECUTE query(100, 200); Index Cond: ((id > $1) AND (id < $2)) Planning time: 0.197 ms Execution time: 0.225 ms -(5 rows) +(6 rows) </programlisting> </para> diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 387d263e8736db1ffa179aa6057e7fa2078a84b8..3fcc1ddc69c5babb72bc77c07228762bc158a43f 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -191,6 +191,9 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("EXPLAIN option TIMING requires ANALYZE"))); + /* currently, summary option is not exposed to users; just set it */ + es.summary = es.analyze; + /* * Parse analysis was done already, but we still have to run the rule * rewriter. We do not do AcquireRewriteLocks: we assume the query either @@ -431,7 +434,8 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, /* * We always collect timing for the entire statement, even when node-level - * timing is off, so we don't look at es->timing here. + * timing is off, so we don't look at es->timing here. (We could skip + * this if !es->summary, but it's hardly worth the complication.) */ INSTR_TIME_SET_CURRENT(starttime); @@ -493,7 +497,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, /* Create textual dump of plan tree */ ExplainPrintPlan(es, queryDesc); - if (es->costs && planduration) + if (es->summary && planduration) { double plantime = INSTR_TIME_GET_DOUBLE(*planduration); @@ -526,7 +530,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, totaltime += elapsed_time(&starttime); - if (es->analyze) + if (es->summary) { if (es->format == EXPLAIN_FORMAT_TEXT) appendStringInfo(es->str, "Execution time: %.3f ms\n", diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index 3488be39c38a819621909f156c4c92606eb9f0c2..d56beaac15989ec836dfaa88763e85aead210040 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -30,9 +30,10 @@ typedef struct ExplainState /* options */ bool verbose; /* be verbose */ bool analyze; /* print actual times */ - bool costs; /* print costs */ + bool costs; /* print estimated costs */ bool buffers; /* print buffer usage */ - bool timing; /* print timing */ + bool timing; /* print detailed node timing */ + bool summary; /* print total planning and execution timing */ ExplainFormat format; /* output format */ /* other states */ PlannedStmt *pstmt; /* top of plan */