1 /*-------------------------------------------------------------------------
3 * pgsp_explain.c: extracted code from explain.c for explain of triggers.
5 * Copyright (c) 2008-2020, PostgreSQL Global Development Group
6 * Copyright (c) 2012-2021, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
9 * pg_store_plans/pgsp_explain.c
11 *-------------------------------------------------------------------------
15 #include "commands/explain.h"
16 #include "utils/rel.h"
17 #include "utils/lsyscache.h"
18 #include "utils/json.h"
19 #include "pgsp_explain.h"
21 static void pgspExplainOpenGroup(const char *objtype, const char *labelname,
22 bool labeled, ExplainState *es);
23 static void pgspExplainCloseGroup(const char *objtype, const char *labelname,
24 bool labeled, ExplainState *es);
25 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
27 static void pgspExplainPropertyText(const char *qlabel, const char *value, ExplainState *es);
28 static void pgspExplainPropertyFloat(const char *qlabel, double value, int ndigits,
30 static void pgspExplainProperty(const char *qlabel, const char *value, bool numeric,
32 static void pgspExplainJSONLineEnding(ExplainState *es);
35 * ExplainState is modified at 9.4.1 and 9.3.6. But the change is for
36 * internal use and to avoid binary-incompatibility not changing the
37 * size of ExplainState. So we can use ExplainState->extra as if it
38 * were grouping_stack safely and should do so. Using ->extra as List*
39 * discards the memory for ExplainStateExtra but it is not a problem
40 * since it is allocated by palloc.
42 #if (PG_VERSION_NUM >= 90401 && PG_VERSION_NUM < 90500) || \
43 (PG_VERSION_NUM >= 90306 && PG_VERSION_NUM < 90400)
44 #define GROUPING_STACK(es) (*((List **)(&(es)->extra)))
46 #define GROUPING_STACK(es) ((es)->grouping_stack)
49 /* ExplainInitState() is replaced with NewExlainState() in 9.5 */
50 #if PG_VERSION_NUM < 90500
54 ExplainState *es = (ExplainState *)palloc0(sizeof(ExplainState));
63 pgspExplainTriggers(ExplainState *es, QueryDesc *queryDesc)
69 #if PG_VERSION_NUM < 140000
70 int numrels = queryDesc->estate->es_num_result_relations;
76 List *targrels = queryDesc->estate->es_trig_target_relations;
79 #if PG_VERSION_NUM >= 140000
80 resultrels = queryDesc->estate->es_opened_result_relations;
81 routerels = queryDesc->estate->es_tuple_routing_result_relations;
82 targrels = queryDesc->estate->es_trig_target_relations;
85 pgspExplainOpenGroup("Triggers", "Triggers", false, es);
87 #if PG_VERSION_NUM < 140000
88 show_relname = (numrels > 1 || targrels != NIL);
89 rInfo = queryDesc->estate->es_result_relations;
90 for (nr = 0; nr < numrels; rInfo++, nr++)
92 show_relname = (list_length(resultrels) > 1 ||
93 routerels != NIL || targrels != NIL);
94 foreach(l, resultrels)
96 rInfo = (ResultRelInfo *) lfirst(l);
98 report_triggers(rInfo, show_relname, es);
99 #if PG_VERSION_NUM >= 140000
102 foreach(l, routerels)
104 rInfo = (ResultRelInfo *) lfirst(l);
105 report_triggers(rInfo, show_relname, es);
111 rInfo = (ResultRelInfo *) lfirst(l);
112 report_triggers(rInfo, show_relname, es);
115 pgspExplainCloseGroup("Triggers", "Triggers", false, es);
120 pgspExplainOpenGroup(const char *objtype, const char *labelname,
121 bool labeled, ExplainState *es)
123 pgspExplainJSONLineEnding(es);
124 appendStringInfoSpaces(es->str, 2 * es->indent);
127 escape_json(es->str, labelname);
128 appendStringInfoString(es->str, ": ");
130 appendStringInfoChar(es->str, labeled ? '{' : '[');
132 GROUPING_STACK(es) = lcons_int(0, GROUPING_STACK(es));
137 pgspExplainCloseGroup(const char *objtype, const char *labelname,
138 bool labeled, ExplainState *es)
141 appendStringInfoChar(es->str, '\n');
142 appendStringInfoSpaces(es->str, 2 * es->indent);
143 appendStringInfoChar(es->str, labeled ? '}' : ']');
144 GROUPING_STACK(es) = list_delete_first(GROUPING_STACK(es));
148 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
152 if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
154 for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
156 Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
157 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
159 char *conname = NULL;
161 /* Must clean up instrumentation state */
165 * We ignore triggers that were never invoked; they likely aren't
166 * relevant to the current query type.
168 if (instr->ntuples == 0)
171 pgspExplainOpenGroup("Trigger", NULL, true, es);
173 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
174 if (OidIsValid(trig->tgconstraint))
175 conname = get_constraint_name(trig->tgconstraint);
177 pgspExplainPropertyText("Trigger Name", trig->tgname, es);
179 pgspExplainPropertyText("Constraint Name", conname, es);
180 pgspExplainPropertyText("Relation", relname, es);
181 pgspExplainPropertyFloat("Time", 1000.0 * instr->total, 3, es);
182 pgspExplainPropertyFloat("Calls", instr->ntuples, 0, es);
187 pgspExplainCloseGroup("Trigger", NULL, true, es);
192 pgspExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
194 pgspExplainProperty(qlabel, value, false, es);
198 pgspExplainPropertyFloat(const char *qlabel, double value, int ndigits,
203 snprintf(buf, sizeof(buf), "%.*f", ndigits, value);
204 pgspExplainProperty(qlabel, buf, true, es);
209 pgspExplainProperty(const char *qlabel, const char *value, bool numeric,
212 pgspExplainJSONLineEnding(es);
213 appendStringInfoSpaces(es->str, es->indent * 2);
214 escape_json(es->str, qlabel);
215 appendStringInfoString(es->str, ": ");
217 appendStringInfoString(es->str, value);
219 escape_json(es->str, value);
223 pgspExplainJSONLineEnding(ExplainState *es)
225 Assert(es->format == EXPLAIN_FORMAT_JSON);
226 if (linitial_int(GROUPING_STACK(es)) != 0)
227 appendStringInfoChar(es->str, ',');
229 linitial_int(GROUPING_STACK(es)) = 1;
230 appendStringInfoChar(es->str, '\n');