2121#include "tcop/pquery.h"
2222#include "utils/lsyscache.h"
2323#include "utils/memdebug.h"
24+ #include "utils/memutils.h"
2425
2526
2627static void printtup_startup (DestReceiver * self , int operation ,
@@ -61,6 +62,7 @@ typedef struct
6162 TupleDesc attrinfo ; /* The attr info we are set up for */
6263 int nattrs ;
6364 PrinttupAttrInfo * myinfo ; /* Cached info about each attr */
65+ MemoryContext tmpcontext ; /* Memory context for per-row workspace */
6466} DR_printtup ;
6567
6668/* ----------------
@@ -87,6 +89,7 @@ printtup_create_DR(CommandDest dest)
8789 self -> attrinfo = NULL ;
8890 self -> nattrs = 0 ;
8991 self -> myinfo = NULL ;
92+ self -> tmpcontext = NULL ;
9093
9194 return (DestReceiver * ) self ;
9295}
@@ -124,6 +127,18 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
124127 DR_printtup * myState = (DR_printtup * ) self ;
125128 Portal portal = myState -> portal ;
126129
130+ /*
131+ * Create a temporary memory context that we can reset once per row to
132+ * recover palloc'd memory. This avoids any problems with leaks inside
133+ * datatype output routines, and should be faster than retail pfree's
134+ * anyway.
135+ */
136+ myState -> tmpcontext = AllocSetContextCreate (CurrentMemoryContext ,
137+ "printtup" ,
138+ ALLOCSET_DEFAULT_MINSIZE ,
139+ ALLOCSET_DEFAULT_INITSIZE ,
140+ ALLOCSET_DEFAULT_MAXSIZE );
141+
127142 if (PG_PROTOCOL_MAJOR (FrontendProtocol ) < 3 )
128143 {
129144 /*
@@ -289,6 +304,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
289304{
290305 TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
291306 DR_printtup * myState = (DR_printtup * ) self ;
307+ MemoryContext oldcontext ;
292308 StringInfoData buf ;
293309 int natts = typeinfo -> natts ;
294310 int i ;
@@ -300,8 +316,11 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
300316 /* Make sure the tuple is fully deconstructed */
301317 slot_getallattrs (slot );
302318
319+ /* Switch into per-row context so we can recover memory below */
320+ oldcontext = MemoryContextSwitchTo (myState -> tmpcontext );
321+
303322 /*
304- * Prepare a DataRow message
323+ * Prepare a DataRow message (note buffer is in per-row context)
305324 */
306325 pq_beginmessage (& buf , 'D' );
307326
@@ -313,8 +332,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
313332 for (i = 0 ; i < natts ; ++ i )
314333 {
315334 PrinttupAttrInfo * thisState = myState -> myinfo + i ;
316- Datum origattr = slot -> tts_values [i ],
317- attr ;
335+ Datum attr = slot -> tts_values [i ];
318336
319337 if (slot -> tts_isnull [i ])
320338 {
@@ -323,30 +341,15 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
323341 }
324342
325343 /*
326- * If we have a toasted datum, forcibly detoast it here to avoid
327- * memory leakage inside the type's output routine.
328- *
329- * Here we catch undefined bytes in tuples that are returned to the
344+ * Here we catch undefined bytes in datums that are returned to the
330345 * client without hitting disk; see comments at the related check in
331- * PageAddItem(). Whether to test before or after detoast is somewhat
332- * arbitrary, as is whether to test external/compressed data at all.
333- * Undefined bytes in the pre-toast datum will have triggered Valgrind
334- * errors in the compressor or toaster; any error detected here for
335- * such datums would indicate an (unlikely) bug in a type-independent
336- * facility. Therefore, this test is most useful for uncompressed,
337- * non-external datums.
338- *
339- * We don't presently bother checking non-varlena datums for undefined
340- * data. PageAddItem() does check them.
346+ * PageAddItem(). This test is most useful for uncompressed,
347+ * non-external datums, but we're quite likely to see such here when
348+ * testing new C functions.
341349 */
342350 if (thisState -> typisvarlena )
343- {
344- VALGRIND_CHECK_MEM_IS_DEFINED (origattr , VARSIZE_ANY (origattr ));
345-
346- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
347- }
348- else
349- attr = origattr ;
351+ VALGRIND_CHECK_MEM_IS_DEFINED (DatumGetPointer (attr ),
352+ VARSIZE_ANY (attr ));
350353
351354 if (thisState -> format == 0 )
352355 {
@@ -355,7 +358,6 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
355358
356359 outputstr = OutputFunctionCall (& thisState -> finfo , attr );
357360 pq_sendcountedtext (& buf , outputstr , strlen (outputstr ), false);
358- pfree (outputstr );
359361 }
360362 else
361363 {
@@ -366,15 +368,14 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
366368 pq_sendint (& buf , VARSIZE (outputbytes ) - VARHDRSZ , 4 );
367369 pq_sendbytes (& buf , VARDATA (outputbytes ),
368370 VARSIZE (outputbytes ) - VARHDRSZ );
369- pfree (outputbytes );
370371 }
371-
372- /* Clean up detoasted copy, if any */
373- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
374- pfree (DatumGetPointer (attr ));
375372 }
376373
377374 pq_endmessage (& buf );
375+
376+ /* Return to caller's context, and flush row's temporary memory */
377+ MemoryContextSwitchTo (oldcontext );
378+ MemoryContextReset (myState -> tmpcontext );
378379}
379380
380381/* ----------------
@@ -386,6 +387,7 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
386387{
387388 TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
388389 DR_printtup * myState = (DR_printtup * ) self ;
390+ MemoryContext oldcontext ;
389391 StringInfoData buf ;
390392 int natts = typeinfo -> natts ;
391393 int i ,
@@ -399,6 +401,9 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
399401 /* Make sure the tuple is fully deconstructed */
400402 slot_getallattrs (slot );
401403
404+ /* Switch into per-row context so we can recover memory below */
405+ oldcontext = MemoryContextSwitchTo (myState -> tmpcontext );
406+
402407 /*
403408 * tell the frontend to expect new tuple data (in ASCII style)
404409 */
@@ -430,34 +435,23 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
430435 for (i = 0 ; i < natts ; ++ i )
431436 {
432437 PrinttupAttrInfo * thisState = myState -> myinfo + i ;
433- Datum origattr = slot -> tts_values [i ],
434- attr ;
438+ Datum attr = slot -> tts_values [i ];
435439 char * outputstr ;
436440
437441 if (slot -> tts_isnull [i ])
438442 continue ;
439443
440444 Assert (thisState -> format == 0 );
441445
442- /*
443- * If we have a toasted datum, forcibly detoast it here to avoid
444- * memory leakage inside the type's output routine.
445- */
446- if (thisState -> typisvarlena )
447- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
448- else
449- attr = origattr ;
450-
451446 outputstr = OutputFunctionCall (& thisState -> finfo , attr );
452447 pq_sendcountedtext (& buf , outputstr , strlen (outputstr ), true);
453- pfree (outputstr );
454-
455- /* Clean up detoasted copy, if any */
456- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
457- pfree (DatumGetPointer (attr ));
458448 }
459449
460450 pq_endmessage (& buf );
451+
452+ /* Return to caller's context, and flush row's temporary memory */
453+ MemoryContextSwitchTo (oldcontext );
454+ MemoryContextReset (myState -> tmpcontext );
461455}
462456
463457/* ----------------
@@ -474,6 +468,10 @@ printtup_shutdown(DestReceiver *self)
474468 myState -> myinfo = NULL ;
475469
476470 myState -> attrinfo = NULL ;
471+
472+ if (myState -> tmpcontext )
473+ MemoryContextDelete (myState -> tmpcontext );
474+ myState -> tmpcontext = NULL ;
477475}
478476
479477/* ----------------
@@ -536,39 +534,23 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
536534 TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
537535 int natts = typeinfo -> natts ;
538536 int i ;
539- Datum origattr ,
540- attr ;
537+ Datum attr ;
541538 char * value ;
542539 bool isnull ;
543540 Oid typoutput ;
544541 bool typisvarlena ;
545542
546543 for (i = 0 ; i < natts ; ++ i )
547544 {
548- origattr = slot_getattr (slot , i + 1 , & isnull );
545+ attr = slot_getattr (slot , i + 1 , & isnull );
549546 if (isnull )
550547 continue ;
551548 getTypeOutputInfo (typeinfo -> attrs [i ]-> atttypid ,
552549 & typoutput , & typisvarlena );
553550
554- /*
555- * If we have a toasted datum, forcibly detoast it here to avoid
556- * memory leakage inside the type's output routine.
557- */
558- if (typisvarlena )
559- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
560- else
561- attr = origattr ;
562-
563551 value = OidOutputFunctionCall (typoutput , attr );
564552
565553 printatt ((unsigned ) i + 1 , typeinfo -> attrs [i ], value );
566-
567- pfree (value );
568-
569- /* Clean up detoasted copy, if any */
570- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
571- pfree (DatumGetPointer (attr ));
572554 }
573555 printf ("\t----\n" );
574556}
@@ -587,6 +569,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
587569{
588570 TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
589571 DR_printtup * myState = (DR_printtup * ) self ;
572+ MemoryContext oldcontext ;
590573 StringInfoData buf ;
591574 int natts = typeinfo -> natts ;
592575 int i ,
@@ -600,6 +583,9 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
600583 /* Make sure the tuple is fully deconstructed */
601584 slot_getallattrs (slot );
602585
586+ /* Switch into per-row context so we can recover memory below */
587+ oldcontext = MemoryContextSwitchTo (myState -> tmpcontext );
588+
603589 /*
604590 * tell the frontend to expect new tuple data (in binary style)
605591 */
@@ -631,35 +617,23 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
631617 for (i = 0 ; i < natts ; ++ i )
632618 {
633619 PrinttupAttrInfo * thisState = myState -> myinfo + i ;
634- Datum origattr = slot -> tts_values [i ],
635- attr ;
620+ Datum attr = slot -> tts_values [i ];
636621 bytea * outputbytes ;
637622
638623 if (slot -> tts_isnull [i ])
639624 continue ;
640625
641626 Assert (thisState -> format == 1 );
642627
643- /*
644- * If we have a toasted datum, forcibly detoast it here to avoid
645- * memory leakage inside the type's output routine.
646- */
647- if (thisState -> typisvarlena )
648- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
649- else
650- attr = origattr ;
651-
652628 outputbytes = SendFunctionCall (& thisState -> finfo , attr );
653- /* We assume the result will not have been toasted */
654629 pq_sendint (& buf , VARSIZE (outputbytes ) - VARHDRSZ , 4 );
655630 pq_sendbytes (& buf , VARDATA (outputbytes ),
656631 VARSIZE (outputbytes ) - VARHDRSZ );
657- pfree (outputbytes );
658-
659- /* Clean up detoasted copy, if any */
660- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
661- pfree (DatumGetPointer (attr ));
662632 }
663633
664634 pq_endmessage (& buf );
635+
636+ /* Return to caller's context, and flush row's temporary memory */
637+ MemoryContextSwitchTo (oldcontext );
638+ MemoryContextReset (myState -> tmpcontext );
665639}
0 commit comments