diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index e29c2ae206..ae078d86de 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -13,6 +13,8 @@ */ #include "postgres.h" +#include + #include "access/xact.h" #include "catalog/pg_type.h" #include "commands/createas.h" @@ -54,6 +56,9 @@ explain_get_index_name_hook_type explain_get_index_name_hook = NULL; #define X_CLOSE_IMMEDIATE 2 #define X_NOWHITESPACE 4 +/* Check if float/double has any decimal number */ +#define HAS_DECIMAL(x) (floor(x) != x) + static void ExplainOneQuery(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, @@ -1643,16 +1648,29 @@ ExplainNode(PlanState *planstate, List *ancestors, double total_ms = 1000.0 * planstate->instrument->total / nloops; double rows = planstate->instrument->ntuples / nloops; + /* + * If the number of loops is greater than one, display the + * actual rows up to two decimal places instead of rounding + * off the value. + */ if (es->format == EXPLAIN_FORMAT_TEXT) { if (es->timing) + { + appendStringInfo(es->str, " (actual time=%.3f..%.3f", + startup_ms, total_ms); appendStringInfo(es->str, - " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)", - startup_ms, total_ms, rows, nloops); + (nloops == 1 || !HAS_DECIMAL(rows)) ? + " rows=%.0f loops=%.0f)" : " rows=%.2f loops=%.0f)", + rows, nloops); + } else + { appendStringInfo(es->str, - " (actual rows=%.0f loops=%.0f)", + (nloops == 1 || !HAS_DECIMAL(rows)) ? + " (actual rows=%.0f loops=%.0f)" : " (actual rows=%.2f loops=%.0f)", rows, nloops); + } } else { @@ -1663,7 +1681,7 @@ ExplainNode(PlanState *planstate, List *ancestors, ExplainPropertyFloat("Actual Total Time", "ms", total_ms, 3, es); } - ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es); + ExplainPropertyFloat("Actual Rows", NULL, rows, (nloops == 1 || !HAS_DECIMAL(rows)) ? 0 : 2, es); ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es); } } @@ -1707,18 +1725,30 @@ ExplainNode(PlanState *planstate, List *ancestors, rows = instrument->ntuples / nloops; ExplainOpenWorker(n, es); + /* + * If the number of loops is greater than one, display the + * actual rows up to two decimal places instead of rounding + * off the value. + */ if (es->format == EXPLAIN_FORMAT_TEXT) { ExplainIndentText(es); if (es->timing) + { + appendStringInfo(es->str, "actual time=%.3f..%.3f", + startup_ms, total_ms); appendStringInfo(es->str, - "actual time=%.3f..%.3f rows=%.0f loops=%.0f\n", - startup_ms, total_ms, rows, nloops); + (nloops == 1 || !HAS_DECIMAL(rows)) ? + " rows=%.0f loops=%.0f" : " rows=%.2f loops=%.0f", + rows, nloops); + } else - appendStringInfo(es->str, - "actual rows=%.0f loops=%.0f\n", + { + appendStringInfo(es->str, (nloops == 1 || !HAS_DECIMAL(rows)) ? + "actual rows=%.0f loops=%.0f" : "actual rows=%.2f loops=%.0f", rows, nloops); + } } else { @@ -1729,7 +1759,8 @@ ExplainNode(PlanState *planstate, List *ancestors, ExplainPropertyFloat("Actual Total Time", "ms", total_ms, 3, es); } - ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es); + ExplainPropertyFloat("Actual Rows", NULL, rows, + (nloops == 1 || !HAS_DECIMAL(rows)) ? 0 : 2, es); ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es); } diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 7555764c77..002e5cc33d 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -2635,14 +2635,14 @@ order by tbl1.col1, tprt.col1; insert into tbl1 values (1001), (1010), (1011); explain (analyze, costs off, summary off, timing off) select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1; - QUERY PLAN --------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Nested Loop (actual rows=23 loops=1) -> Seq Scan on tbl1 (actual rows=5 loops=1) - -> Append (actual rows=5 loops=5) + -> Append (actual rows=4.60 loops=5) -> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=5) Index Cond: (col1 < tbl1.col1) - -> Index Scan using tprt2_idx on tprt_2 (actual rows=3 loops=4) + -> Index Scan using tprt2_idx on tprt_2 (actual rows=2.75 loops=4) Index Cond: (col1 < tbl1.col1) -> Index Scan using tprt3_idx on tprt_3 (actual rows=1 loops=2) Index Cond: (col1 < tbl1.col1) @@ -2656,16 +2656,16 @@ select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1; explain (analyze, costs off, summary off, timing off) select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1; - QUERY PLAN --------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Nested Loop (actual rows=3 loops=1) -> Seq Scan on tbl1 (actual rows=5 loops=1) - -> Append (actual rows=1 loops=5) + -> Append (actual rows=0.60 loops=5) -> Index Scan using tprt1_idx on tprt_1 (never executed) Index Cond: (col1 = tbl1.col1) -> Index Scan using tprt2_idx on tprt_2 (actual rows=1 loops=2) Index Cond: (col1 = tbl1.col1) - -> Index Scan using tprt3_idx on tprt_3 (actual rows=0 loops=3) + -> Index Scan using tprt3_idx on tprt_3 (actual rows=0.33 loops=3) Index Cond: (col1 = tbl1.col1) -> Index Scan using tprt4_idx on tprt_4 (never executed) Index Cond: (col1 = tbl1.col1)