Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * explain_dr.c
4 : : * Explain DestReceiver to measure serialization overhead
5 : : *
6 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994-5, Regents of the University of California
8 : : *
9 : : * IDENTIFICATION
10 : : * src/backend/commands/explain_dr.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "commands/explain.h"
17 : : #include "commands/explain_dr.h"
18 : : #include "commands/explain_state.h"
19 : : #include "libpq/pqformat.h"
20 : : #include "libpq/protocol.h"
21 : : #include "utils/lsyscache.h"
22 : : #include "varatt.h"
23 : :
24 : : /*
25 : : * DestReceiver functions for SERIALIZE option
26 : : *
27 : : * A DestReceiver for query tuples, that serializes passed rows into RowData
28 : : * messages while measuring the resources expended and total serialized size,
29 : : * while never sending the data to the client. This allows measuring the
30 : : * overhead of deTOASTing and datatype out/sendfuncs, which are not otherwise
31 : : * exercisable without actually hitting the network.
32 : : */
33 : : typedef struct SerializeDestReceiver
34 : : {
35 : : DestReceiver pub;
36 : : ExplainState *es; /* this EXPLAIN statement's ExplainState */
37 : : int8 format; /* text or binary, like pq wire protocol */
38 : : TupleDesc attrinfo; /* the output tuple desc */
39 : : int nattrs; /* current number of columns */
40 : : FmgrInfo *finfos; /* precomputed call info for output fns */
41 : : MemoryContext tmpcontext; /* per-row temporary memory context */
42 : : StringInfoData buf; /* buffer to hold the constructed message */
43 : : SerializeMetrics metrics; /* collected metrics */
44 : : } SerializeDestReceiver;
45 : :
46 : : /*
47 : : * Get the function lookup info that we'll need for output.
48 : : *
49 : : * This is a subset of what printtup_prepare_info() does. We don't need to
50 : : * cope with format choices varying across columns, so it's slightly simpler.
51 : : */
52 : : static void
242 rhaas@postgresql.org 53 :CBC 12 : serialize_prepare_info(SerializeDestReceiver *receiver,
54 : : TupleDesc typeinfo, int nattrs)
55 : : {
56 : : /* get rid of any old data */
57 [ - + ]: 12 : if (receiver->finfos)
242 rhaas@postgresql.org 58 :UBC 0 : pfree(receiver->finfos);
242 rhaas@postgresql.org 59 :CBC 12 : receiver->finfos = NULL;
60 : :
61 : 12 : receiver->attrinfo = typeinfo;
62 : 12 : receiver->nattrs = nattrs;
63 [ - + ]: 12 : if (nattrs <= 0)
242 rhaas@postgresql.org 64 :UBC 0 : return;
65 : :
242 rhaas@postgresql.org 66 :CBC 12 : receiver->finfos = (FmgrInfo *) palloc0(nattrs * sizeof(FmgrInfo));
67 : :
68 [ + + ]: 36 : for (int i = 0; i < nattrs; i++)
69 : : {
70 : 24 : FmgrInfo *finfo = receiver->finfos + i;
71 : 24 : Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
72 : : Oid typoutput;
73 : : Oid typsend;
74 : : bool typisvarlena;
75 : :
76 [ + + ]: 24 : if (receiver->format == 0)
77 : : {
78 : : /* wire protocol format text */
79 : 18 : getTypeOutputInfo(attr->atttypid,
80 : : &typoutput,
81 : : &typisvarlena);
82 : 18 : fmgr_info(typoutput, finfo);
83 : : }
84 [ + - ]: 6 : else if (receiver->format == 1)
85 : : {
86 : : /* wire protocol format binary */
87 : 6 : getTypeBinaryOutputInfo(attr->atttypid,
88 : : &typsend,
89 : : &typisvarlena);
90 : 6 : fmgr_info(typsend, finfo);
91 : : }
92 : : else
242 rhaas@postgresql.org 93 [ # # ]:UBC 0 : ereport(ERROR,
94 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
95 : : errmsg("unsupported format code: %d", receiver->format)));
96 : : }
97 : : }
98 : :
99 : : /*
100 : : * serializeAnalyzeReceive - collect tuples for EXPLAIN (SERIALIZE)
101 : : *
102 : : * This should match printtup() in printtup.c as closely as possible,
103 : : * except for the addition of measurement code.
104 : : */
105 : : static bool
242 rhaas@postgresql.org 106 :CBC 60 : serializeAnalyzeReceive(TupleTableSlot *slot, DestReceiver *self)
107 : : {
108 : 60 : TupleDesc typeinfo = slot->tts_tupleDescriptor;
109 : 60 : SerializeDestReceiver *myState = (SerializeDestReceiver *) self;
110 : : MemoryContext oldcontext;
111 : 60 : StringInfo buf = &myState->buf;
112 : 60 : int natts = typeinfo->natts;
113 : : instr_time start,
114 : : end;
115 : : BufferUsage instr_start;
116 : :
117 : : /* only measure time, buffers if requested */
118 [ + + ]: 60 : if (myState->es->timing)
119 : 45 : INSTR_TIME_SET_CURRENT(start);
120 [ + + ]: 60 : if (myState->es->buffers)
121 : 45 : instr_start = pgBufferUsage;
122 : :
123 : : /* Set or update my derived attribute info, if needed */
124 [ + + - + ]: 60 : if (myState->attrinfo != typeinfo || myState->nattrs != natts)
125 : 12 : serialize_prepare_info(myState, typeinfo, natts);
126 : :
127 : : /* Make sure the tuple is fully deconstructed */
128 : 60 : slot_getallattrs(slot);
129 : :
130 : : /* Switch into per-row context so we can recover memory below */
131 : 60 : oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
132 : :
133 : : /*
134 : : * Prepare a DataRow message (note buffer is in per-query context)
135 : : *
136 : : * Note that we fill a StringInfo buffer the same as printtup() does, so
137 : : * as to capture the costs of manipulating the strings accurately.
138 : : */
139 : 60 : pq_beginmessage_reuse(buf, PqMsg_DataRow);
140 : :
141 : 60 : pq_sendint16(buf, natts);
142 : :
143 : : /*
144 : : * send the attributes of this tuple
145 : : */
146 [ + + ]: 180 : for (int i = 0; i < natts; i++)
147 : : {
148 : 120 : FmgrInfo *finfo = myState->finfos + i;
149 : 120 : Datum attr = slot->tts_values[i];
150 : :
151 [ - + ]: 120 : if (slot->tts_isnull[i])
152 : : {
242 rhaas@postgresql.org 153 :UBC 0 : pq_sendint32(buf, -1);
154 : 0 : continue;
155 : : }
156 : :
242 rhaas@postgresql.org 157 [ + + ]:CBC 120 : if (myState->format == 0)
158 : : {
159 : : /* Text output */
160 : : char *outputstr;
161 : :
162 : 90 : outputstr = OutputFunctionCall(finfo, attr);
163 : 90 : pq_sendcountedtext(buf, outputstr, strlen(outputstr));
164 : : }
165 : : else
166 : : {
167 : : /* Binary output */
168 : : bytea *outputbytes;
169 : :
170 : 30 : outputbytes = SendFunctionCall(finfo, attr);
171 : 30 : pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
172 : 30 : pq_sendbytes(buf, VARDATA(outputbytes),
173 : 30 : VARSIZE(outputbytes) - VARHDRSZ);
174 : : }
175 : : }
176 : :
177 : : /*
178 : : * We mustn't call pq_endmessage_reuse(), since that would actually send
179 : : * the data to the client. Just count the data, instead. We can leave
180 : : * the buffer alone; it'll be reset on the next iteration (as would also
181 : : * happen in printtup()).
182 : : */
183 : 60 : myState->metrics.bytesSent += buf->len;
184 : :
185 : : /* Return to caller's context, and flush row's temporary memory */
186 : 60 : MemoryContextSwitchTo(oldcontext);
187 : 60 : MemoryContextReset(myState->tmpcontext);
188 : :
189 : : /* Update timing data */
190 [ + + ]: 60 : if (myState->es->timing)
191 : : {
192 : 45 : INSTR_TIME_SET_CURRENT(end);
193 : 45 : INSTR_TIME_ACCUM_DIFF(myState->metrics.timeSpent, end, start);
194 : : }
195 : :
196 : : /* Update buffer metrics */
197 [ + + ]: 60 : if (myState->es->buffers)
198 : 45 : BufferUsageAccumDiff(&myState->metrics.bufferUsage,
199 : : &pgBufferUsage,
200 : : &instr_start);
201 : :
202 : 60 : return true;
203 : : }
204 : :
205 : : /*
206 : : * serializeAnalyzeStartup - start up the serializeAnalyze receiver
207 : : */
208 : : static void
209 : 12 : serializeAnalyzeStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
210 : : {
211 : 12 : SerializeDestReceiver *receiver = (SerializeDestReceiver *) self;
212 : :
213 [ - + ]: 12 : Assert(receiver->es != NULL);
214 : :
215 [ - + + - ]: 12 : switch (receiver->es->serialize)
216 : : {
242 rhaas@postgresql.org 217 :UBC 0 : case EXPLAIN_SERIALIZE_NONE:
218 : 0 : Assert(false);
219 : : break;
242 rhaas@postgresql.org 220 :CBC 9 : case EXPLAIN_SERIALIZE_TEXT:
221 : 9 : receiver->format = 0; /* wire protocol format text */
222 : 9 : break;
223 : 3 : case EXPLAIN_SERIALIZE_BINARY:
224 : 3 : receiver->format = 1; /* wire protocol format binary */
225 : 3 : break;
226 : : }
227 : :
228 : : /* Create per-row temporary memory context */
229 : 12 : receiver->tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
230 : : "SerializeTupleReceive",
231 : : ALLOCSET_DEFAULT_SIZES);
232 : :
233 : : /* The output buffer is re-used across rows, as in printtup.c */
234 : 12 : initStringInfo(&receiver->buf);
235 : :
236 : : /* Initialize results counters */
237 : 12 : memset(&receiver->metrics, 0, sizeof(SerializeMetrics));
238 : 12 : INSTR_TIME_SET_ZERO(receiver->metrics.timeSpent);
239 : 12 : }
240 : :
241 : : /*
242 : : * serializeAnalyzeShutdown - shut down the serializeAnalyze receiver
243 : : */
244 : : static void
245 : 12 : serializeAnalyzeShutdown(DestReceiver *self)
246 : : {
247 : 12 : SerializeDestReceiver *receiver = (SerializeDestReceiver *) self;
248 : :
249 [ + - ]: 12 : if (receiver->finfos)
250 : 12 : pfree(receiver->finfos);
251 : 12 : receiver->finfos = NULL;
252 : :
253 [ + - ]: 12 : if (receiver->buf.data)
254 : 12 : pfree(receiver->buf.data);
255 : 12 : receiver->buf.data = NULL;
256 : :
257 [ + - ]: 12 : if (receiver->tmpcontext)
258 : 12 : MemoryContextDelete(receiver->tmpcontext);
259 : 12 : receiver->tmpcontext = NULL;
260 : 12 : }
261 : :
262 : : /*
263 : : * serializeAnalyzeDestroy - destroy the serializeAnalyze receiver
264 : : */
265 : : static void
266 : 12 : serializeAnalyzeDestroy(DestReceiver *self)
267 : : {
268 : 12 : pfree(self);
269 : 12 : }
270 : :
271 : : /*
272 : : * Build a DestReceiver for EXPLAIN (SERIALIZE) instrumentation.
273 : : */
274 : : DestReceiver *
275 : 12 : CreateExplainSerializeDestReceiver(ExplainState *es)
276 : : {
277 : : SerializeDestReceiver *self;
278 : :
279 : 12 : self = (SerializeDestReceiver *) palloc0(sizeof(SerializeDestReceiver));
280 : :
281 : 12 : self->pub.receiveSlot = serializeAnalyzeReceive;
282 : 12 : self->pub.rStartup = serializeAnalyzeStartup;
283 : 12 : self->pub.rShutdown = serializeAnalyzeShutdown;
284 : 12 : self->pub.rDestroy = serializeAnalyzeDestroy;
285 : 12 : self->pub.mydest = DestExplainSerialize;
286 : :
287 : 12 : self->es = es;
288 : :
289 : 12 : return (DestReceiver *) self;
290 : : }
291 : :
292 : : /*
293 : : * GetSerializationMetrics - collect metrics
294 : : *
295 : : * We have to be careful here since the receiver could be an IntoRel
296 : : * receiver if the subject statement is CREATE TABLE AS. In that
297 : : * case, return all-zeroes stats.
298 : : */
299 : : SerializeMetrics
300 : 15 : GetSerializationMetrics(DestReceiver *dest)
301 : : {
302 : : SerializeMetrics empty;
303 : :
304 [ + + ]: 15 : if (dest->mydest == DestExplainSerialize)
305 : 12 : return ((SerializeDestReceiver *) dest)->metrics;
306 : :
307 : 3 : memset(&empty, 0, sizeof(SerializeMetrics));
308 : 3 : INSTR_TIME_SET_ZERO(empty.timeSpent);
309 : :
310 : 3 : return empty;
311 : : }
|