#include "libpq/pqformat.h"
#include "tcop/pquery.h"
#include "utils/lsyscache.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])
{
continue;
}
- /*
- * 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;
-
if (thisState->format == 0)
{
/* Text output */
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);
}
column_info->column_type = column_type;
}
- /*
- * If we have a toasted datum, forcibly detoast it here to avoid
- * memory leakage inside the type's output routine.
- */
- if (column_info->typisvarlena)
- attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
- else
- attr = values[i];
-
+ attr = values[i];
value = OutputFunctionCall(&column_info->proc, attr);
/* Detect whether we need double quotes for this value */
}
if (nq)
appendStringInfoCharMacro(&buf, '"');
-
- pfree(value);
-
- /* Clean up detoasted copy, if any */
- if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
- pfree(DatumGetPointer(attr));
}
appendStringInfoChar(&buf, ')');
column_info->column_type = column_type;
}
- /*
- * If we have a toasted datum, forcibly detoast it here to avoid
- * memory leakage inside the type's output routine.
- */
- if (column_info->typisvarlena)
- attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
- else
- attr = values[i];
-
+ attr = values[i];
outputbytes = SendFunctionCall(&column_info->proc, 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(values[i]))
- pfree(DatumGetPointer(attr));
}
pfree(values);