OSDN Git Service

Follow new shared memory request convention since PG15
[pgstoreplans/pg_store_plans.git] / pgsp_explain.c
1 /*-------------------------------------------------------------------------
2  *
3  * pgsp_explain.c: extracted code from explain.c for explain of triggers.
4  *
5  * Copyright (c) 2008-2020, PostgreSQL Global Development Group
6  * Copyright (c) 2012-2021, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
7  *
8  * IDENTIFICATION
9  *        pg_store_plans/pgsp_explain.c
10  *
11  *-------------------------------------------------------------------------
12  */
13
14 #include "postgres.h"
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"
20
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,
26                                          ExplainState *es);
27 static void pgspExplainPropertyText(const char *qlabel, const char *value, ExplainState *es);
28 static void pgspExplainPropertyFloat(const char *qlabel, double value, int ndigits,
29                                                   ExplainState *es);
30 static void pgspExplainProperty(const char *qlabel, const char *value, bool numeric,
31                                                         ExplainState *es);
32 static void pgspExplainJSONLineEnding(ExplainState *es);
33
34 /*
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.
41  */
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)))
45 #else
46 #define GROUPING_STACK(es) ((es)->grouping_stack)
47 #endif
48
49 /* ExplainInitState() is replaced with NewExlainState() in 9.5 */
50 #if PG_VERSION_NUM < 90500
51 ExplainState *
52 NewExplainState(void)
53 {
54   ExplainState *es = (ExplainState *)palloc0(sizeof(ExplainState));
55
56   ExplainInitState(es);
57   es->costs = true;
58   return es;
59 }
60 #endif
61
62 void
63 pgspExplainTriggers(ExplainState *es, QueryDesc *queryDesc)
64 {
65         if (es->analyze)
66         {
67                 ResultRelInfo *rInfo;
68                 bool            show_relname;
69 #if PG_VERSION_NUM < 140000
70                 int         numrels = queryDesc->estate->es_num_result_relations;
71                 int         nr;
72 #else
73                 List       *resultrels;
74                 List       *routerels;
75 #endif
76                 List       *targrels = queryDesc->estate->es_trig_target_relations;
77                 ListCell   *l;
78
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;
83 #endif
84
85                 pgspExplainOpenGroup("Triggers", "Triggers", false, es);
86
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++)
91 #else
92                 show_relname = (list_length(resultrels) > 1 ||
93                                                 routerels != NIL || targrels != NIL);
94                 foreach(l, resultrels)
95                 {
96                         rInfo = (ResultRelInfo *) lfirst(l);
97 #endif
98                         report_triggers(rInfo, show_relname, es);
99 #if PG_VERSION_NUM >= 140000
100                 }
101
102                 foreach(l, routerels)
103                 {
104                         rInfo = (ResultRelInfo *) lfirst(l);
105                         report_triggers(rInfo, show_relname, es);
106                 }
107 #endif
108
109                 foreach(l, targrels)
110                 {
111                         rInfo = (ResultRelInfo *) lfirst(l);
112                         report_triggers(rInfo, show_relname, es);
113                 }
114
115                 pgspExplainCloseGroup("Triggers", "Triggers", false, es);
116         }
117 }
118
119 static void
120 pgspExplainOpenGroup(const char *objtype, const char *labelname,
121                                  bool labeled, ExplainState *es)
122 {
123         pgspExplainJSONLineEnding(es);
124         appendStringInfoSpaces(es->str, 2 * es->indent);
125         if (labelname)
126         {
127                 escape_json(es->str, labelname);
128                 appendStringInfoString(es->str, ": ");
129         }
130         appendStringInfoChar(es->str, labeled ? '{' : '[');
131
132         GROUPING_STACK(es) = lcons_int(0, GROUPING_STACK(es));
133         es->indent++;
134 }
135
136 static void
137 pgspExplainCloseGroup(const char *objtype, const char *labelname,
138                                   bool labeled, ExplainState *es)
139 {
140         es->indent--;
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));
145 }
146
147 static void
148 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
149 {
150         int                     nt;
151
152         if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
153                 return;
154         for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
155         {
156                 Trigger    *trig = rInfo->ri_TrigDesc->triggers + nt;
157                 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
158                 char       *relname;
159                 char       *conname = NULL;
160
161                 /* Must clean up instrumentation state */
162                 InstrEndLoop(instr);
163
164                 /*
165                  * We ignore triggers that were never invoked; they likely aren't
166                  * relevant to the current query type.
167                  */
168                 if (instr->ntuples == 0)
169                         continue;
170
171                 pgspExplainOpenGroup("Trigger", NULL, true, es);
172
173                 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
174                 if (OidIsValid(trig->tgconstraint))
175                         conname = get_constraint_name(trig->tgconstraint);
176
177                 pgspExplainPropertyText("Trigger Name", trig->tgname, es);
178                 if (conname)
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);
183
184                 if (conname)
185                         pfree(conname);
186
187                 pgspExplainCloseGroup("Trigger", NULL, true, es);
188         }
189 }
190
191 static void
192 pgspExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
193 {
194         pgspExplainProperty(qlabel, value, false, es);
195 }
196
197 static void
198 pgspExplainPropertyFloat(const char *qlabel, double value, int ndigits,
199                                          ExplainState *es)
200 {
201         char            buf[256];
202
203         snprintf(buf, sizeof(buf), "%.*f", ndigits, value);
204         pgspExplainProperty(qlabel, buf, true, es);
205 }
206
207
208 static void
209 pgspExplainProperty(const char *qlabel, const char *value, bool numeric,
210                                 ExplainState *es)
211 {
212         pgspExplainJSONLineEnding(es);
213         appendStringInfoSpaces(es->str, es->indent * 2);
214         escape_json(es->str, qlabel);
215         appendStringInfoString(es->str, ": ");
216         if (numeric)
217                 appendStringInfoString(es->str, value);
218         else
219                 escape_json(es->str, value);
220 }
221
222 static void
223 pgspExplainJSONLineEnding(ExplainState *es)
224 {
225         Assert(es->format == EXPLAIN_FORMAT_JSON);
226         if (linitial_int(GROUPING_STACK(es)) != 0)
227                 appendStringInfoChar(es->str, ',');
228         else
229                 linitial_int(GROUPING_STACK(es)) = 1;
230         appendStringInfoChar(es->str, '\n');
231 }
232