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
6162TupleDesc attrinfo ;/* The attr info we are set up for */
6263int nattrs ;
6364PrinttupAttrInfo * 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)
8789self -> attrinfo = NULL ;
8890self -> nattrs = 0 ;
8991self -> myinfo = NULL ;
92+ self -> tmpcontext = NULL ;
9093
9194return (DestReceiver * )self ;
9295}
@@ -124,6 +127,18 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
124127DR_printtup * myState = (DR_printtup * )self ;
125128Portal 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+
127142if (PG_PROTOCOL_MAJOR (FrontendProtocol )< 3 )
128143{
129144/*
@@ -289,6 +304,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
289304{
290305TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
291306DR_printtup * myState = (DR_printtup * )self ;
307+ MemoryContext oldcontext ;
292308StringInfoData buf ;
293309int natts = typeinfo -> natts ;
294310int i ;
@@ -300,8 +316,11 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
300316/* Make sure the tuple is fully deconstructed */
301317slot_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 */
306325pq_beginmessage (& buf ,'D' );
307326
@@ -313,8 +332,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
313332for (i = 0 ;i < natts ;++ i )
314333{
315334PrinttupAttrInfo * thisState = myState -> myinfo + i ;
316- Datum origattr = slot -> tts_values [i ],
317- attr ;
335+ Datum attr = slot -> tts_values [i ];
318336
319337if (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 */
342350if (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
351354if (thisState -> format == 0 )
352355{
@@ -355,7 +358,6 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
355358
356359outputstr = OutputFunctionCall (& thisState -> finfo ,attr );
357360pq_sendcountedtext (& buf ,outputstr ,strlen (outputstr ), false);
358- pfree (outputstr );
359361}
360362else
361363{
@@ -366,15 +368,14 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
366368pq_sendint (& buf ,VARSIZE (outputbytes )- VARHDRSZ ,4 );
367369pq_sendbytes (& buf ,VARDATA (outputbytes ),
368370VARSIZE (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
377374pq_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{
387388TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
388389DR_printtup * myState = (DR_printtup * )self ;
390+ MemoryContext oldcontext ;
389391StringInfoData buf ;
390392int natts = typeinfo -> natts ;
391393int i ,
@@ -399,6 +401,9 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
399401/* Make sure the tuple is fully deconstructed */
400402slot_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)
430435for (i = 0 ;i < natts ;++ i )
431436{
432437PrinttupAttrInfo * thisState = myState -> myinfo + i ;
433- Datum origattr = slot -> tts_values [i ],
434- attr ;
438+ Datum attr = slot -> tts_values [i ];
435439char * outputstr ;
436440
437441if (slot -> tts_isnull [i ])
438442continue ;
439443
440444Assert (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-
451446outputstr = OutputFunctionCall (& thisState -> finfo ,attr );
452447pq_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
460450pq_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)
474468myState -> myinfo = NULL ;
475469
476470myState -> 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)
536534TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
537535int natts = typeinfo -> natts ;
538536int i ;
539- Datum origattr ,
540- attr ;
537+ Datum attr ;
541538char * value ;
542539bool isnull ;
543540Oid typoutput ;
544541bool typisvarlena ;
545542
546543for (i = 0 ;i < natts ;++ i )
547544{
548- origattr = slot_getattr (slot ,i + 1 ,& isnull );
545+ attr = slot_getattr (slot ,i + 1 ,& isnull );
549546if (isnull )
550547continue ;
551548getTypeOutputInfo (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-
563551value = OidOutputFunctionCall (typoutput ,attr );
564552
565553printatt ((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}
573555printf ("\t----\n" );
574556}
@@ -587,6 +569,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
587569{
588570TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
589571DR_printtup * myState = (DR_printtup * )self ;
572+ MemoryContext oldcontext ;
590573StringInfoData buf ;
591574int natts = typeinfo -> natts ;
592575int i ,
@@ -600,6 +583,9 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
600583/* Make sure the tuple is fully deconstructed */
601584slot_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)
631617for (i = 0 ;i < natts ;++ i )
632618{
633619PrinttupAttrInfo * thisState = myState -> myinfo + i ;
634- Datum origattr = slot -> tts_values [i ],
635- attr ;
620+ Datum attr = slot -> tts_values [i ];
636621bytea * outputbytes ;
637622
638623if (slot -> tts_isnull [i ])
639624continue ;
640625
641626Assert (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-
652628outputbytes = SendFunctionCall (& thisState -> finfo ,attr );
653- /* We assume the result will not have been toasted */
654629pq_sendint (& buf ,VARSIZE (outputbytes )- VARHDRSZ ,4 );
655630pq_sendbytes (& buf ,VARDATA (outputbytes ),
656631VARSIZE (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
664634pq_endmessage (& buf );
635+
636+ /* Return to caller's context, and flush row's temporary memory */
637+ MemoryContextSwitchTo (oldcontext );
638+ MemoryContextReset (myState -> tmpcontext );
665639}