From 18578e3d07aa159631e0903abae496a6482fa279 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= <josef.simanek@gmail.com>
Date: Sat, 27 Jul 2019 09:27:41 +0200
Subject: [PATCH] Show possible triggers in EXPLAIN.

---
 doc/src/sgml/ref/explain.sgml                 |  9 ++++
 src/backend/commands/explain.c                | 43 +++++++++++++++----
 src/include/commands/explain.h                |  1 +
 src/test/regress/expected/foreign_key.out     |  6 ++-
 src/test/regress/expected/insert_conflict.out |  4 +-
 src/test/regress/expected/updatable_views.out | 10 ++++-
 6 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/doc/src/sgml/ref/explain.sgml b/doc/src/sgml/ref/explain.sgml
index 385d10411fa0..ba0d8df88c19 100644
--- a/doc/src/sgml/ref/explain.sgml
+++ b/doc/src/sgml/ref/explain.sgml
@@ -43,6 +43,7 @@ EXPLAIN [ ANALYZE ] [ VERBOSE ] <replaceable class="parameter">statement</replac
     BUFFERS [ <replaceable class="parameter">boolean</replaceable> ]
     TIMING [ <replaceable class="parameter">boolean</replaceable> ]
     SUMMARY [ <replaceable class="parameter">boolean</replaceable> ]
+    TRIGGERS [ <replaceable class="parameter">boolean</replaceable> ]
     FORMAT { TEXT | XML | JSON | YAML }
 </synopsis>
  </refsynopsisdiv>
@@ -224,6 +225,14 @@ ROLLBACK;
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>TRIGGERS</literal></term>
+    <listitem>
+     <para>
+       TODO
+     </para>
+    </listitem>
+   </varlistentry>
    <varlistentry>
     <term><literal>FORMAT</literal></term>
     <listitem>
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 62fb3434a32f..93845bcaad36 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -149,6 +149,7 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
 	ListCell   *lc;
 	bool		timing_set = false;
 	bool		summary_set = false;
+	bool		triggers_set = false;
 
 	/* Parse options list. */
 	foreach(lc, stmt->options)
@@ -175,6 +176,11 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
 			summary_set = true;
 			es->summary = defGetBoolean(opt);
 		}
+		else if (strcmp(opt->defname, "triggers") == 0)
+		{
+			triggers_set = true;
+			es->triggers = defGetBoolean(opt);
+		}
 		else if (strcmp(opt->defname, "format") == 0)
 		{
 			char	   *p = defGetString(opt);
@@ -210,6 +216,9 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
 	/* if the timing was not set explicitly, set default value */
 	es->timing = (timing_set) ? es->timing : es->analyze;
 
+	/* if the triggers was not set explicitly, set default value */
+	es->triggers = (triggers_set) ? es->triggers : true;
+
 	/* check that timing is used with EXPLAIN ANALYZE */
 	if (es->timing && !es->analyze)
 		ereport(ERROR,
@@ -556,7 +565,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 	}
 
 	/* Print info about runtime of triggers */
-	if (es->analyze)
+	if (es->triggers)
 		ExplainPrintTriggers(es, queryDesc);
 
 	/*
@@ -911,17 +920,23 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
 {
 	int			nt;
 
-	if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
+	if (!rInfo->ri_TrigDesc || (es->analyze && !rInfo->ri_TrigInstrument))
 		return;
 	for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
 	{
 		Trigger    *trig = rInfo->ri_TrigDesc->triggers + nt;
-		Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
+		Instrumentation *instr;
 		char	   *relname;
 		char	   *conname = NULL;
 
 		/* Must clean up instrumentation state */
-		InstrEndLoop(instr);
+		if (es->analyze) {
+			instr = rInfo->ri_TrigInstrument + nt;
+			InstrEndLoop(instr);
+
+			if (instr->ntuples == 0)
+				continue;
+		}
 
 		/*
 		 * We ignore triggers that were never invoked; they likely aren't
@@ -951,11 +966,15 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
 				appendStringInfo(es->str, " for constraint %s", conname);
 			if (show_relname)
 				appendStringInfo(es->str, " on %s", relname);
-			if (es->timing)
-				appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
-								 1000.0 * instr->total, instr->ntuples);
+			if (es->analyze)
+			{
+				if (es->timing)
+					appendStringInfo(es->str, ": time=%.3f calls=%.0f",
+						1000.0 * instr->total, instr->ntuples);
 			else
-				appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples);
+				appendStringInfo(es->str, ": calls=%.0f", instr->ntuples);
+			}
+			appendStringInfoString(es->str, "\n");
 		}
 		else
 		{
@@ -966,7 +985,13 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
 			if (es->timing)
 				ExplainPropertyFloat("Time", "ms", 1000.0 * instr->total, 3,
 									 es);
-			ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es);
+			if (es->analyze)
+			{
+				if (es->timing)
+					ExplainPropertyFloat("Time", "ms", 1000.0 * instr->total, 3,
+						es);
+				ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es);
+			}
 		}
 
 		if (conname)
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index 8639891c1643..7c67e4745b00 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -35,6 +35,7 @@ typedef struct ExplainState
 	bool		buffers;		/* print buffer usage */
 	bool		timing;			/* print detailed node timing */
 	bool		summary;		/* print total planning and execution timing */
+	bool            triggers;               /* print triggers */
 	bool		settings;		/* print modified settings */
 	ExplainFormat format;		/* output format */
 	/* state for output formatting --- not reset for each new plan tree */
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index 894084f94f14..2eb68575de39 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -1421,11 +1421,15 @@ explain (costs off) delete from t1 where a = 1;
                Index Cond: (a = 1)
          ->  Seq Scan on t2
                Filter: (b = 1)
+ Trigger for constraint t2_b_fkey
+ Trigger for constraint t2_b_fkey
  
  Delete on t1
    ->  Index Scan using t1_pkey on t1
          Index Cond: (a = 1)
-(10 rows)
+ Trigger for constraint t2_b_fkey
+ Trigger for constraint t2_b_fkey
+(14 rows)
 
 delete from t1 where a = 1;
 -- Test a primary key with attributes located in later attnum positions
diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out
index 1338b2b23e17..94a0fefc3a41 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -218,7 +218,9 @@ explain (costs off, format json) insert into insertconflicttest values (0, 'Bilb
            "Parallel Aware": false                                     +
          }                                                             +
        ]                                                               +
-     }                                                                 +
+     },                                                                +
+     "Triggers": [                                                     +
+     ]                                                                 +
    }                                                                   +
  ]
 (1 row)
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 8443c24f18b7..162fac6904c4 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -899,7 +899,10 @@ EXPLAIN (costs off) UPDATE rw_view2 SET a=3 WHERE a=2;
                Recheck Cond: (a > 0)
                ->  Bitmap Index Scan on base_tbl_pkey
                      Index Cond: (a > 0)
-(7 rows)
+ Trigger rw_view1_del_trig
+ Trigger rw_view1_ins_trig
+ Trigger rw_view1_upd_trig
+(10 rows)
 
 EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2;
                         QUERY PLAN                        
@@ -911,7 +914,10 @@ EXPLAIN (costs off) DELETE FROM rw_view2 WHERE a=2;
                Recheck Cond: (a > 0)
                ->  Bitmap Index Scan on base_tbl_pkey
                      Index Cond: (a > 0)
-(7 rows)
+ Trigger rw_view1_del_trig
+ Trigger rw_view1_ins_trig
+ Trigger rw_view1_upd_trig
+(10 rows)
 
 DROP TABLE base_tbl CASCADE;
 NOTICE:  drop cascades to 2 other objects
