From 4b61e5ae514f5e5bc430bcf1132597ba4ecd202b Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Tue, 31 Dec 2019 18:49:41 -0600
Subject: [PATCH v1 2/2] explain analyze to show stats from (hash) aggregate..

..as suggested by Jeff Janes
---
 src/backend/commands/explain.c      | 41 +++++++++++++++++++++++++++++--------
 src/backend/executor/execGrouping.c | 10 +++++++++
 src/include/nodes/execnodes.h       | 27 ++++++++++++------------
 3 files changed, 57 insertions(+), 21 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 11b5857..9493e7d 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -18,6 +18,7 @@
 #include "commands/createas.h"
 #include "commands/defrem.h"
 #include "commands/prepare.h"
+#include "executor/nodeAgg.h"
 #include "executor/nodeHash.h"
 #include "foreign/fdwapi.h"
 #include "jit/jit.h"
@@ -84,6 +85,7 @@ static void show_sort_keys(SortState *sortstate, List *ancestors,
 						   ExplainState *es);
 static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
 								   ExplainState *es);
+static void show_agg_info(AggState *astate, ExplainState *es);
 static void show_agg_keys(AggState *astate, List *ancestors,
 						  ExplainState *es);
 static void show_grouping_sets(PlanState *planstate, Agg *agg,
@@ -103,7 +105,7 @@ static void show_sortorder_options(StringInfo buf, Node *sortexpr,
 static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
 							 List *ancestors, ExplainState *es);
 static void show_sort_info(SortState *sortstate, ExplainState *es);
-static void show_hinstrument(ExplainState *es, HashInstrumentation *h);
+static void show_hinstrument(ExplainState *es, HashInstrumentation *h, bool showbatch);
 static void show_hash_info(HashState *hashstate, ExplainState *es);
 static void show_tidbitmap_info(BitmapHeapScanState *planstate,
 								ExplainState *es);
@@ -1889,6 +1891,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			if (plan->qual)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
+			show_agg_info(castNode(AggState, planstate), es);
+
 			break;
 		case T_Group:
 			show_group_keys(castNode(GroupState, planstate), ancestors, es);
@@ -2072,6 +2076,21 @@ ExplainNode(PlanState *planstate, List *ancestors,
 }
 
 /*
+ * Show instrumentation info for an Agg node.
+ */
+static void
+show_agg_info(AggState *astate, ExplainState *es)
+{
+	// perhash->aggnode->numGroups; memctx; AggState->
+
+	for (int i=0; i<astate->num_hashes; ++i) {
+		HashInstrumentation *hinstrument = &astate->perhash->hashtable->hinstrument;
+// fprintf(stderr, "memallocated %lu\n", astate->hashcontext->ecxt_per_query_memory->mem_allocated);
+		show_hinstrument(es, hinstrument, false);
+	}
+}
+
+/*
  * Show the targetlist of a plan node
  */
 static void
@@ -2714,14 +2733,14 @@ show_hash_info(HashState *hashstate, ExplainState *es)
 		}
 	}
 
-	show_hinstrument(es, &hinstrument);
+	show_hinstrument(es, &hinstrument, true);
 }
 
 /*
  * Show hash bucket stats and (optionally) memory.
  */
 static void
-show_hinstrument(ExplainState *es, HashInstrumentation *h)
+show_hinstrument(ExplainState *es, HashInstrumentation *h, bool showbatch)
 {
 	long		spacePeakKb = (h->space_peak + 1023) / 1024;
 
@@ -2755,9 +2774,12 @@ show_hinstrument(ExplainState *es, HashInstrumentation *h)
 			 h->nbuckets_original != h->nbuckets) {
 			ExplainIndentText(es);
 			appendStringInfo(es->str,
-						"Buckets: %d (originally %d)   Batches: %d (originally %d)",
+						"Buckets: %d (originally %d)",
 						h->nbuckets,
-						h->nbuckets_original,
+						h->nbuckets_original);
+			if (showbatch)
+				appendStringInfo(es->str,
+						"  Batches: %d (originally %d)",
 						h->nbatch,
 						h->nbatch_original);
 		}
@@ -2765,9 +2787,12 @@ show_hinstrument(ExplainState *es, HashInstrumentation *h)
 		{
 			ExplainIndentText(es);
 			appendStringInfo(es->str,
-						"Buckets: %d  Batches: %d",
-						h->nbuckets,
-						h->nbatch);
+						"Buckets: %d",
+						h->nbuckets);
+				if (showbatch)
+					appendStringInfo(es->str,
+							"  Batches: %d",
+							h->nbatch);
 		}
 
 		if (es->verbose && es->analyze)
diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c
index 3603c58..cf0fe3c 100644
--- a/src/backend/executor/execGrouping.c
+++ b/src/backend/executor/execGrouping.c
@@ -203,6 +203,11 @@ BuildTupleHashTableExt(PlanState *parent,
 		hashtable->hash_iv = 0;
 
 	hashtable->hashtab = tuplehash_create(metacxt, nbuckets, hashtable);
+	hashtable->hinstrument.nbuckets_original = nbuckets;
+	hashtable->hinstrument.nbuckets = nbuckets;
+	hashtable->hinstrument.space_peak = entrysize * hashtable->hashtab->size;
+	hashtable->hinstrument.nbatch_original = 1; /* Unused */
+	hashtable->hinstrument.nbatch = 1; /* Unused */
 
 	/*
 	 * We copy the input tuple descriptor just for safety --- we assume all
@@ -328,6 +333,11 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
 		{
 			/* created new entry */
 			*isnew = true;
+			/* maybe grew ? XXX: need to call max() here ? */
+			hashtable->hinstrument.nbuckets = hashtable->hashtab->size;
+			hashtable->hinstrument.space_peak = sizeof(TupleHashEntryData) * hashtable->hashtab->size +
+				sizeof(MinimalTuple) * hashtable->hashtab->size; // members ?
+
 			/* zero caller data */
 			entry->additional = NULL;
 			MemoryContextSwitchTo(hashtable->tablecxt);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1f6f5bb..913416f 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -671,6 +671,19 @@ typedef struct ExecAuxRowMark
 typedef struct TupleHashEntryData *TupleHashEntry;
 typedef struct TupleHashTableData *TupleHashTable;
 
+/* ----------------
+ *	 Values displayed by EXPLAIN ANALYZE
+ * ----------------
+ */
+typedef struct HashInstrumentation
+{
+	int			nbuckets;		/* number of buckets at end of execution */
+	int			nbuckets_original;	/* planned number of buckets */
+	int			nbatch;			/* number of batches at end of execution */
+	int			nbatch_original;	/* planned number of batches */
+	size_t		space_peak;		/* peak memory usage in bytes */
+} HashInstrumentation;
+
 typedef struct TupleHashEntryData
 {
 	MinimalTuple firstTuple;	/* copy of first tuple in this group */
@@ -705,6 +718,7 @@ typedef struct TupleHashTableData
 	ExprState  *cur_eq_func;	/* comparator for input vs. table */
 	uint32		hash_iv;		/* hash-function IV */
 	ExprContext *exprcontext;	/* expression context */
+	HashInstrumentation hinstrument;	/* instrumentation for EXPLAIN */
 }			TupleHashTableData;
 
 typedef tuplehash_iterator TupleHashIterator;
@@ -2243,19 +2257,6 @@ typedef struct GatherMergeState
 } GatherMergeState;
 
 /* ----------------
- *	 Values displayed by EXPLAIN ANALYZE
- * ----------------
- */
-typedef struct HashInstrumentation
-{
-	int			nbuckets;		/* number of buckets at end of execution */
-	int			nbuckets_original;	/* planned number of buckets */
-	int			nbatch;			/* number of batches at end of execution */
-	int			nbatch_original;	/* planned number of batches */
-	size_t		space_peak;		/* peak memory usage in bytes */
-} HashInstrumentation;
-
-/* ----------------
  *	 Shared memory container for per-worker hash information
  * ----------------
  */
-- 
2.7.4

