#include "tcop/pquery.h"
#include "utils/lsyscache.h"
#include "utils/memdebug.h"
+#include "utils/memutils.h"
static void printtup_startup(DestReceiver *self, int operation,
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;
/* ----------------
self->attrinfo = NULL;
self->nattrs = 0;
self->myinfo = NULL;
+ self->tmpcontext = NULL;
return (DestReceiver *) self;
}
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)
{
/*
{
TupleDesc typeinfo = slot->tts_tupleDescriptor;
DR_printtup *myState = (DR_printtup *) self;
+ MemoryContext oldcontext;
StringInfoData buf;
int natts = typeinfo->natts;
int i;
/* 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');
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])
{
}
/*
- * 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)
{
outputstr = OutputFunctionCall(&thisState->finfo, attr);
pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
- pfree(outputstr);
}
else
{
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);
}
/* ----------------
{
TupleDesc typeinfo = slot->tts_tupleDescriptor;
DR_printtup *myState = (DR_printtup *) self;
+ MemoryContext oldcontext;
StringInfoData buf;
int natts = typeinfo->natts;
int i,
/* 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)
*/
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])
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);
}
/* ----------------
myState->myinfo = NULL;
myState->attrinfo = NULL;
+
+ if (myState->tmpcontext)
+ MemoryContextDelete(myState->tmpcontext);
+ myState->tmpcontext = NULL;
}
/* ----------------
TupleDesc typeinfo = slot->tts_tupleDescriptor;
int natts = typeinfo->natts;
int i;
- Datum origattr,
- attr;
+ Datum attr;
char *value;
bool isnull;
Oid typoutput;
for (i = 0; i < natts; ++i)
{
- origattr = 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");
}
{
TupleDesc typeinfo = slot->tts_tupleDescriptor;
DR_printtup *myState = (DR_printtup *) self;
+ MemoryContext oldcontext;
StringInfoData buf;
int natts = typeinfo->natts;
int i,
/* 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)
*/
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])
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);
}