OSDN Git Service

Add the LICENSE file and update copyright notice of all files
[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-2019, PostgreSQL Global Development Group
6  * Copyright (c) 2012-2019, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
7  *
8  * IDENTIFICATION
9  *        pg_store_plan/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                 int                     numrels = queryDesc->estate->es_num_result_relations;
70                 List       *targrels = queryDesc->estate->es_trig_target_relations;
71                 int                     nr;
72                 ListCell   *l;
73                 
74                 pgspExplainOpenGroup("Triggers", "Triggers", false, es);
75                 
76                 show_relname = (numrels > 1 || targrels != NIL);
77                 rInfo = queryDesc->estate->es_result_relations;
78                 for (nr = 0; nr < numrels; rInfo++, nr++)
79                         report_triggers(rInfo, show_relname, es);
80                 
81                 foreach(l, targrels)
82                 {
83                         rInfo = (ResultRelInfo *) lfirst(l);
84                         report_triggers(rInfo, show_relname, es);
85                 }
86                 
87                 pgspExplainCloseGroup("Triggers", "Triggers", false, es);
88         }
89 }
90
91 static void
92 pgspExplainOpenGroup(const char *objtype, const char *labelname,
93                                  bool labeled, ExplainState *es)
94 {
95         pgspExplainJSONLineEnding(es);
96         appendStringInfoSpaces(es->str, 2 * es->indent);
97         if (labelname)
98         {
99                 escape_json(es->str, labelname);
100                 appendStringInfoString(es->str, ": ");
101         }
102         appendStringInfoChar(es->str, labeled ? '{' : '[');
103         
104         GROUPING_STACK(es) = lcons_int(0, GROUPING_STACK(es));
105         es->indent++;
106 }
107
108 static void
109 pgspExplainCloseGroup(const char *objtype, const char *labelname,
110                                   bool labeled, ExplainState *es)
111 {
112         es->indent--;
113         appendStringInfoChar(es->str, '\n');
114         appendStringInfoSpaces(es->str, 2 * es->indent);
115         appendStringInfoChar(es->str, labeled ? '}' : ']');
116         GROUPING_STACK(es) = list_delete_first(GROUPING_STACK(es));
117 }
118
119 static void
120 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
121 {
122         int                     nt;
123
124         if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
125                 return;
126         for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
127         {
128                 Trigger    *trig = rInfo->ri_TrigDesc->triggers + nt;
129                 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
130                 char       *relname;
131                 char       *conname = NULL;
132
133                 /* Must clean up instrumentation state */
134                 InstrEndLoop(instr);
135
136                 /*
137                  * We ignore triggers that were never invoked; they likely aren't
138                  * relevant to the current query type.
139                  */
140                 if (instr->ntuples == 0)
141                         continue;
142
143                 pgspExplainOpenGroup("Trigger", NULL, true, es);
144
145                 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
146                 if (OidIsValid(trig->tgconstraint))
147                         conname = get_constraint_name(trig->tgconstraint);
148
149                 pgspExplainPropertyText("Trigger Name", trig->tgname, es);
150                 if (conname)
151                         pgspExplainPropertyText("Constraint Name", conname, es);
152                 pgspExplainPropertyText("Relation", relname, es);
153                 pgspExplainPropertyFloat("Time", 1000.0 * instr->total, 3, es);
154                 pgspExplainPropertyFloat("Calls", instr->ntuples, 0, es);
155
156                 if (conname)
157                         pfree(conname);
158
159                 pgspExplainCloseGroup("Trigger", NULL, true, es);
160         }
161 }
162
163 static void
164 pgspExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
165 {
166         pgspExplainProperty(qlabel, value, false, es);
167 }
168
169 static void
170 pgspExplainPropertyFloat(const char *qlabel, double value, int ndigits,
171                                          ExplainState *es)
172 {
173         char            buf[256];
174
175         snprintf(buf, sizeof(buf), "%.*f", ndigits, value);
176         pgspExplainProperty(qlabel, buf, true, es);
177 }
178
179
180 static void
181 pgspExplainProperty(const char *qlabel, const char *value, bool numeric,
182                                 ExplainState *es)
183 {
184         pgspExplainJSONLineEnding(es);
185         appendStringInfoSpaces(es->str, es->indent * 2);
186         escape_json(es->str, qlabel);
187         appendStringInfoString(es->str, ": ");
188         if (numeric)
189                 appendStringInfoString(es->str, value);
190         else
191                 escape_json(es->str, value);
192 }
193
194 static void
195 pgspExplainJSONLineEnding(ExplainState *es)
196 {
197         Assert(es->format == EXPLAIN_FORMAT_JSON);
198         if (linitial_int(GROUPING_STACK(es)) != 0)
199                 appendStringInfoChar(es->str, ',');
200         else
201                 linitial_int(GROUPING_STACK(es)) = 1;
202         appendStringInfoChar(es->str, '\n');
203 }
204