diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 2819e479f8..aaec439892 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -120,6 +120,7 @@ static void show_sort_group_keys(PlanState *planstate, const char *qlabel, List *ancestors, ExplainState *es); static void show_sortorder_options(StringInfo buf, Node *sortexpr, Oid sortOperator, Oid collation, bool nullsFirst); +static void show_storage_info(Tuplestorestate *tupstore, ExplainState *es); static void show_tablesample(TableSampleClause *tsc, PlanState *planstate, List *ancestors, ExplainState *es); static void show_sort_info(SortState *sortstate, ExplainState *es); @@ -127,6 +128,7 @@ static void show_incremental_sort_info(IncrementalSortState *incrsortstate, ExplainState *es); static void show_hash_info(HashState *hashstate, ExplainState *es); static void show_material_info(MaterialState *mstate, ExplainState *es); +static void show_windowagg_info(WindowAggState *winstate, ExplainState *es); static void show_memoize_info(MemoizeState *mstate, List *ancestors, ExplainState *es); static void show_hashagg_info(AggState *aggstate, ExplainState *es); @@ -2231,6 +2233,7 @@ ExplainNode(PlanState *planstate, List *ancestors, planstate, es); show_upper_qual(((WindowAgg *) plan)->runConditionOrig, "Run Condition", planstate, ancestors, es); + show_windowagg_info(castNode(WindowAggState, planstate), es); break; case T_Group: show_group_keys(castNode(GroupState, planstate), ancestors, es); @@ -2894,6 +2897,34 @@ show_sortorder_options(StringInfo buf, Node *sortexpr, } } +/* + * Show information on storage method and maximum memory/disk space used. + */ +static void +show_storage_info(Tuplestorestate *tupstore, ExplainState *es) +{ + char *maxStorageType; + int64 maxSpaceUsed, + maxSpaceUsedKB; + + tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed); + maxSpaceUsedKB = BYTES_TO_KILOBYTES(maxSpaceUsed); + + if (es->format != EXPLAIN_FORMAT_TEXT) + { + ExplainPropertyText("Storage", maxStorageType, es); + ExplainPropertyInteger("Maximum Storage", "kB", maxSpaceUsedKB, es); + } + else + { + ExplainIndentText(es); + appendStringInfo(es->str, + "Storage: %s Maximum Storage: " INT64_FORMAT "kB\n", + maxStorageType, + maxSpaceUsedKB); + } +} + /* * Show TABLESAMPLE properties */ @@ -3350,9 +3381,6 @@ static void show_material_info(MaterialState *mstate, ExplainState *es) { Tuplestorestate *tupstore = mstate->tuplestorestate; - char *maxStorageType; - int64 maxSpaceUsed, - maxSpaceUsedKB; /* * Nothing to show if ANALYZE option wasn't used or if execution didn't @@ -3361,22 +3389,26 @@ show_material_info(MaterialState *mstate, ExplainState *es) if (!es->analyze || tupstore == NULL) return; - tuplestore_get_stats(tupstore, &maxStorageType, &maxSpaceUsed); - maxSpaceUsedKB = BYTES_TO_KILOBYTES(maxSpaceUsed); + show_storage_info(tupstore, es); +} - if (es->format != EXPLAIN_FORMAT_TEXT) - { - ExplainPropertyText("Storage", maxStorageType, es); - ExplainPropertyInteger("Maximum Storage", "kB", maxSpaceUsedKB, es); - } - else - { - ExplainIndentText(es); - appendStringInfo(es->str, - "Storage: %s Maximum Storage: " INT64_FORMAT "kB\n", - maxStorageType, - maxSpaceUsedKB); - } +/* + * Show information on WindowAgg node, storage method and maximum memory/disk + * space used. + */ +static void +show_windowagg_info(WindowAggState *winstate, ExplainState *es) +{ + Tuplestorestate *tupstore = winstate->buffer; + + /* + * Nothing to show if ANALYZE option wasn't used or if execution didn't + * get as far as creating the tuplestore. + */ + if (!es->analyze || tupstore == NULL) + return; + + show_storage_info(tupstore, es); } /* diff --git a/src/test/regress/expected/explain.out b/src/test/regress/expected/explain.out index 6585c6a69e..d01c304c24 100644 --- a/src/test/regress/expected/explain.out +++ b/src/test/regress/expected/explain.out @@ -691,3 +691,41 @@ select explain_filter('explain (analyze,serialize) create temp table explain_tem Execution Time: N.N ms (4 rows) +-- Test tuplestore storage usage in Window aggregate (memory case) +select explain_filter('explain (analyze,costs off) select sum(n) over() from generate_series(1,10) a(n)'); + explain_filter +-------------------------------------------------------------------------------- + WindowAgg (actual time=N.N..N.N rows=N loops=N) + Storage: Memory Maximum Storage: NkB + -> Function Scan on generate_series a (actual time=N.N..N.N rows=N loops=N) + Planning Time: N.N ms + Execution Time: N.N ms +(5 rows) + +-- Test tuplestore storage usage in Window aggregate (disk case) +set work_mem to 64; +select explain_filter('explain (analyze,costs off) select sum(n) over() from generate_series(1,2000) a(n)'); + explain_filter +-------------------------------------------------------------------------------- + WindowAgg (actual time=N.N..N.N rows=N loops=N) + Storage: Disk Maximum Storage: NkB + -> Function Scan on generate_series a (actual time=N.N..N.N rows=N loops=N) + Planning Time: N.N ms + Execution Time: N.N ms +(5 rows) + +-- Test tuplestore storage usage in Window aggregate (memory and disk case, final result is disk) +select explain_filter('explain (analyze,costs off) select sum(n) over(partition by m) from (SELECT n < 3 as m, n from generate_series(1,2000) a(n))'); + explain_filter +-------------------------------------------------------------------------------------- + WindowAgg (actual time=N.N..N.N rows=N loops=N) + Storage: Disk Maximum Storage: NkB + -> Sort (actual time=N.N..N.N rows=N loops=N) + Sort Key: ((a.n < N)) + Sort Method: external merge Disk: NkB + -> Function Scan on generate_series a (actual time=N.N..N.N rows=N loops=N) + Planning Time: N.N ms + Execution Time: N.N ms +(8 rows) + +reset work_mem; diff --git a/src/test/regress/sql/explain.sql b/src/test/regress/sql/explain.sql index c7055f850c..b861e2b53d 100644 --- a/src/test/regress/sql/explain.sql +++ b/src/test/regress/sql/explain.sql @@ -169,3 +169,12 @@ select explain_filter('explain (analyze,serialize text,buffers,timing off) selec select explain_filter('explain (analyze,serialize binary,buffers,timing) select * from int8_tbl i8'); -- this tests an edge case where we have no data to return select explain_filter('explain (analyze,serialize) create temp table explain_temp as select * from int8_tbl i8'); + +-- Test tuplestore storage usage in Window aggregate (memory case) +select explain_filter('explain (analyze,costs off) select sum(n) over() from generate_series(1,10) a(n)'); +-- Test tuplestore storage usage in Window aggregate (disk case) +set work_mem to 64; +select explain_filter('explain (analyze,costs off) select sum(n) over() from generate_series(1,2000) a(n)'); +-- Test tuplestore storage usage in Window aggregate (memory and disk case, final result is disk) +select explain_filter('explain (analyze,costs off) select sum(n) over(partition by m) from (SELECT n < 3 as m, n from generate_series(1,2000) a(n))'); +reset work_mem;