From d691e492b619e5cc6a1fcd4134728c1c0852d589 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      | 50 +++++++++++++++++++++++++++++++------
 src/backend/executor/execGrouping.c | 10 ++++++++
 src/include/executor/nodeAgg.h      |  1 +
 src/include/nodes/execnodes.h       | 27 ++++++++++----------
 4 files changed, 67 insertions(+), 21 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index d5eaf15..22d6087 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"
@@ -82,6 +83,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,
@@ -101,7 +103,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);
@@ -1848,6 +1850,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);
@@ -2057,6 +2061,23 @@ 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);
+	}
+
+	// TODO
+}
+
+/*
  * Show the targetlist of a plan node
  */
 static void
@@ -2703,19 +2724,26 @@ 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;
 
+	// Currently, this isn't shown for explain of hash(join) since nbatch=0 without analyze
+	// But, it's shown for hashAgg since nbatch=1, always.
+	// Need to 1) avoid showing memory use if !analyze; and, 2) avoid memory use if not verbose
+
 	if (h->nbatch <= 0)
 		return;
+	/* This avoids showing anything if it's explain without analyze; should we just check that, instead ? */
+	if (!es->analyze)
+		return;
 
 	if (es->format != EXPLAIN_FORMAT_TEXT)
 	{
@@ -2736,9 +2764,12 @@ show_hinstrument(ExplainState *es, HashInstrumentation *h)
 			 h->nbuckets_original != h->nbuckets) {
 			appendStringInfoSpaces(es->str, es->indent * 2);
 			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);
 		}
@@ -2746,9 +2777,12 @@ show_hinstrument(ExplainState *es, HashInstrumentation *h)
 		{
 			appendStringInfoSpaces(es->str, es->indent * 2);
 			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/executor/nodeAgg.h b/src/include/executor/nodeAgg.h
index 2fe82da..b2ab74c 100644
--- a/src/include/executor/nodeAgg.h
+++ b/src/include/executor/nodeAgg.h
@@ -302,6 +302,7 @@ typedef struct AggStatePerHashData
 	AttrNumber *hashGrpColIdxInput; /* hash col indices in input slot */
 	AttrNumber *hashGrpColIdxHash;	/* indices in hash table tuples */
 	Agg		   *aggnode;		/* original Agg node, for numGroups etc. */
+	// XXX struct HashInstrumentation hinstrument;
 }			AggStatePerHashData;
 
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index eaea1f3..ef83406 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 */
+	struct HashInstrumentation hinstrument;	/* XXX: NOT A POINTER this worker's entry */
 }			TupleHashTableData;
 
 typedef tuplehash_iterator TupleHashIterator;
@@ -2235,19 +2249,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

