@ -21,6 +21,7 @@
# include "tcop/pquery.h"
# include "utils/lsyscache.h"
# include "utils/memdebug.h"
# include "utils/memutils.h"
static void printtup_startup ( DestReceiver * self , int operation ,
@ -61,6 +62,7 @@ typedef struct
TupleDesc attrinfo ; /* The attr info we are set up for */
int nattrs ;
PrinttupAttrInfo * myinfo ; /* Cached info about each attr */
MemoryContext tmpcontext ; /* Memory context for per-row workspace */
} DR_printtup ;
/* ----------------
@ -87,6 +89,7 @@ printtup_create_DR(CommandDest dest)
self - > attrinfo = NULL ;
self - > nattrs = 0 ;
self - > myinfo = NULL ;
self - > tmpcontext = NULL ;
return ( DestReceiver * ) self ;
}
@ -124,6 +127,18 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
DR_printtup * myState = ( DR_printtup * ) self ;
Portal portal = myState - > portal ;
/*
* Create a temporary memory context that we can reset once per row to
* recover palloc ' d memory . This avoids any problems with leaks inside
* datatype output routines , and should be faster than retail pfree ' s
* anyway .
*/
myState - > tmpcontext = AllocSetContextCreate ( CurrentMemoryContext ,
" printtup " ,
ALLOCSET_DEFAULT_MINSIZE ,
ALLOCSET_DEFAULT_INITSIZE ,
ALLOCSET_DEFAULT_MAXSIZE ) ;
if ( PG_PROTOCOL_MAJOR ( FrontendProtocol ) < 3 )
{
/*
@ -289,6 +304,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
{
TupleDesc typeinfo = slot - > tts_tupleDescriptor ;
DR_printtup * myState = ( DR_printtup * ) self ;
MemoryContext oldcontext ;
StringInfoData buf ;
int natts = typeinfo - > natts ;
int i ;
@ -300,8 +316,11 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
/* Make sure the tuple is fully deconstructed */
slot_getallattrs ( slot ) ;
/* Switch into per-row context so we can recover memory below */
oldcontext = MemoryContextSwitchTo ( myState - > tmpcontext ) ;
/*
* Prepare a DataRow message
* Prepare a DataRow message ( note buffer is in per - row context )
*/
pq_beginmessage ( & buf , ' D ' ) ;
@ -313,8 +332,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
for ( i = 0 ; i < natts ; + + i )
{
PrinttupAttrInfo * thisState = myState - > myinfo + i ;
Datum origattr = slot - > tts_values [ i ] ,
attr ;
Datum attr = slot - > tts_values [ i ] ;
if ( slot - > tts_isnull [ i ] )
{
@ -323,30 +341,15 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
}
/*
* If we have a toasted datum , forcibly detoast it here to avoid
* memory leakage inside the type ' s output routine .
*
* Here we catch undefined bytes in tuples that are returned to the
* Here we catch undefined bytes in datums that are returned to the
* client without hitting disk ; see comments at the related check in
* PageAddItem ( ) . Whether to test before or after detoast is somewhat
* arbitrary , as is whether to test external / compressed data at all .
* Undefined bytes in the pre - toast datum will have triggered Valgrind
* errors in the compressor or toaster ; any error detected here for
* such datums would indicate an ( unlikely ) bug in a type - independent
* facility . Therefore , this test is most useful for uncompressed ,
* non - external datums .
*
* We don ' t presently bother checking non - varlena datums for undefined
* data . PageAddItem ( ) does check them .
* PageAddItem ( ) . This test is most useful for uncompressed ,
* non - external datums , but we ' re quite likely to see such here when
* testing new C functions .
*/
if ( thisState - > typisvarlena )
{
VALGRIND_CHECK_MEM_IS_DEFINED ( origattr , VARSIZE_ANY ( origattr ) ) ;
attr = PointerGetDatum ( PG_DETOAST_DATUM ( origattr ) ) ;
}
else
attr = origattr ;
VALGRIND_CHECK_MEM_IS_DEFINED ( DatumGetPointer ( attr ) ,
VARSIZE_ANY ( attr ) ) ;
if ( thisState - > format = = 0 )
{
@ -355,7 +358,6 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
outputstr = OutputFunctionCall ( & thisState - > finfo , attr ) ;
pq_sendcountedtext ( & buf , outputstr , strlen ( outputstr ) , false ) ;
pfree ( outputstr ) ;
}
else
{
@ -366,15 +368,14 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
pq_sendint ( & buf , VARSIZE ( outputbytes ) - VARHDRSZ , 4 ) ;
pq_sendbytes ( & buf , VARDATA ( outputbytes ) ,
VARSIZE ( outputbytes ) - VARHDRSZ ) ;
pfree ( outputbytes ) ;
}
/* Clean up detoasted copy, if any */
if ( DatumGetPointer ( attr ) ! = DatumGetPointer ( origattr ) )
pfree ( DatumGetPointer ( attr ) ) ;
}
pq_endmessage ( & buf ) ;
/* Return to caller's context, and flush row's temporary memory */
MemoryContextSwitchTo ( oldcontext ) ;
MemoryContextReset ( myState - > tmpcontext ) ;
}
/* ----------------
@ -386,6 +387,7 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
{
TupleDesc typeinfo = slot - > tts_tupleDescriptor ;
DR_printtup * myState = ( DR_printtup * ) self ;
MemoryContext oldcontext ;
StringInfoData buf ;
int natts = typeinfo - > natts ;
int i ,
@ -399,6 +401,9 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
/* Make sure the tuple is fully deconstructed */
slot_getallattrs ( slot ) ;
/* Switch into per-row context so we can recover memory below */
oldcontext = MemoryContextSwitchTo ( myState - > tmpcontext ) ;
/*
* tell the frontend to expect new tuple data ( in ASCII style )
*/
@ -430,8 +435,7 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
for ( i = 0 ; i < natts ; + + i )
{
PrinttupAttrInfo * thisState = myState - > myinfo + i ;
Datum origattr = slot - > tts_values [ i ] ,
attr ;
Datum attr = slot - > tts_values [ i ] ;
char * outputstr ;
if ( slot - > tts_isnull [ i ] )
@ -439,25 +443,15 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
Assert ( thisState - > format = = 0 ) ;
/*
* If we have a toasted datum , forcibly detoast it here to avoid
* memory leakage inside the type ' s output routine .
*/
if ( thisState - > typisvarlena )
attr = PointerGetDatum ( PG_DETOAST_DATUM ( origattr ) ) ;
else
attr = origattr ;
outputstr = OutputFunctionCall ( & thisState - > finfo , attr ) ;
pq_sendcountedtext ( & buf , outputstr , strlen ( outputstr ) , true ) ;
pfree ( outputstr ) ;
/* Clean up detoasted copy, if any */
if ( DatumGetPointer ( attr ) ! = DatumGetPointer ( origattr ) )
pfree ( DatumGetPointer ( attr ) ) ;
}
pq_endmessage ( & buf ) ;
/* Return to caller's context, and flush row's temporary memory */
MemoryContextSwitchTo ( oldcontext ) ;
MemoryContextReset ( myState - > tmpcontext ) ;
}
/* ----------------
@ -474,6 +468,10 @@ printtup_shutdown(DestReceiver *self)
myState - > myinfo = NULL ;
myState - > attrinfo = NULL ;
if ( myState - > tmpcontext )
MemoryContextDelete ( myState - > tmpcontext ) ;
myState - > tmpcontext = NULL ;
}
/* ----------------
@ -536,8 +534,7 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
TupleDesc typeinfo = slot - > tts_tupleDescriptor ;
int natts = typeinfo - > natts ;
int i ;
Datum origattr ,
attr ;
Datum attr ;
char * value ;
bool isnull ;
Oid typoutput ;
@ -545,30 +542,15 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
for ( i = 0 ; i < natts ; + + i )
{
orig attr = slot_getattr ( slot , i + 1 , & isnull ) ;
attr = slot_getattr ( slot , i + 1 , & isnull ) ;
if ( isnull )
continue ;
getTypeOutputInfo ( typeinfo - > attrs [ i ] - > atttypid ,
& typoutput , & typisvarlena ) ;
/*
* If we have a toasted datum , forcibly detoast it here to avoid
* memory leakage inside the type ' s output routine .
*/
if ( typisvarlena )
attr = PointerGetDatum ( PG_DETOAST_DATUM ( origattr ) ) ;
else
attr = origattr ;
value = OidOutputFunctionCall ( typoutput , attr ) ;
printatt ( ( unsigned ) i + 1 , typeinfo - > attrs [ i ] , value ) ;
pfree ( value ) ;
/* Clean up detoasted copy, if any */
if ( DatumGetPointer ( attr ) ! = DatumGetPointer ( origattr ) )
pfree ( DatumGetPointer ( attr ) ) ;
}
printf ( " \t ---- \n " ) ;
}
@ -587,6 +569,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
{
TupleDesc typeinfo = slot - > tts_tupleDescriptor ;
DR_printtup * myState = ( DR_printtup * ) self ;
MemoryContext oldcontext ;
StringInfoData buf ;
int natts = typeinfo - > natts ;
int i ,
@ -600,6 +583,9 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
/* Make sure the tuple is fully deconstructed */
slot_getallattrs ( slot ) ;
/* Switch into per-row context so we can recover memory below */
oldcontext = MemoryContextSwitchTo ( myState - > tmpcontext ) ;
/*
* tell the frontend to expect new tuple data ( in binary style )
*/
@ -631,8 +617,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
for ( i = 0 ; i < natts ; + + i )
{
PrinttupAttrInfo * thisState = myState - > myinfo + i ;
Datum origattr = slot - > tts_values [ i ] ,
attr ;
Datum attr = slot - > tts_values [ i ] ;
bytea * outputbytes ;
if ( slot - > tts_isnull [ i ] )
@ -640,26 +625,15 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
Assert ( thisState - > format = = 1 ) ;
/*
* If we have a toasted datum , forcibly detoast it here to avoid
* memory leakage inside the type ' s output routine .
*/
if ( thisState - > typisvarlena )
attr = PointerGetDatum ( PG_DETOAST_DATUM ( origattr ) ) ;
else
attr = origattr ;
outputbytes = SendFunctionCall ( & thisState - > finfo , attr ) ;
/* We assume the result will not have been toasted */
pq_sendint ( & buf , VARSIZE ( outputbytes ) - VARHDRSZ , 4 ) ;
pq_sendbytes ( & buf , VARDATA ( outputbytes ) ,
VARSIZE ( outputbytes ) - VARHDRSZ ) ;
pfree ( outputbytes ) ;
/* Clean up detoasted copy, if any */
if ( DatumGetPointer ( attr ) ! = DatumGetPointer ( origattr ) )
pfree ( DatumGetPointer ( attr ) ) ;
}
pq_endmessage ( & buf ) ;
/* Return to caller's context, and flush row's temporary memory */
MemoryContextSwitchTo ( oldcontext ) ;
MemoryContextReset ( myState - > tmpcontext ) ;
}