Make tuple receive/print routines TOAST-aware. Formerly, printtup would

leak memory when printing a toasted attribute, and printtup_internal
didn't work at all...
This commit is contained in:
Tom Lane 2000-12-01 22:10:31 +00:00
parent f5371feef9
commit 217d1566bf
4 changed files with 172 additions and 108 deletions

View File

@ -9,12 +9,10 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.54 2000/11/16 22:30:15 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.55 2000/12/01 22:10:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "access/heapam.h" #include "access/heapam.h"
@ -25,6 +23,7 @@
static void printtup_setup(DestReceiver *self, TupleDesc typeinfo); static void printtup_setup(DestReceiver *self, TupleDesc typeinfo);
static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self); static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
static void printtup_cleanup(DestReceiver *self); static void printtup_cleanup(DestReceiver *self);
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
@ -33,15 +32,12 @@ static void printtup_cleanup(DestReceiver *self);
*/ */
/* ---------------- /* ----------------
* getTypeOutAndElem -- get both typoutput and typelem for a type * getTypeOutputInfo -- get info needed for printing values of a type
*
* We used to fetch these with two separate function calls,
* typtoout() and gettypelem(), which each called SearchSysCache.
* This way takes half the time.
* ---------------- * ----------------
*/ */
int bool
getTypeOutAndElem(Oid type, Oid *typOutput, Oid *typElem) getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
bool *typIsVarlena)
{ {
HeapTuple typeTuple; HeapTuple typeTuple;
Form_pg_type pt; Form_pg_type pt;
@ -50,11 +46,12 @@ getTypeOutAndElem(Oid type, Oid *typOutput, Oid *typElem)
ObjectIdGetDatum(type), ObjectIdGetDatum(type),
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(typeTuple)) if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "getTypeOutAndElem: Cache lookup of type %u failed", type); elog(ERROR, "getTypeOutputInfo: Cache lookup of type %u failed", type);
pt = (Form_pg_type) GETSTRUCT(typeTuple); pt = (Form_pg_type) GETSTRUCT(typeTuple);
*typOutput = pt->typoutput; *typOutput = pt->typoutput;
*typElem = pt->typelem; *typElem = pt->typelem;
*typIsVarlena = (! pt->typbyval) && (pt->typlen == -1);
ReleaseSysCache(typeTuple); ReleaseSysCache(typeTuple);
return OidIsValid(*typOutput); return OidIsValid(*typOutput);
} }
@ -67,6 +64,7 @@ typedef struct
{ /* Per-attribute information */ { /* Per-attribute information */
Oid typoutput; /* Oid for the attribute's type output fn */ Oid typoutput; /* Oid for the attribute's type output fn */
Oid typelem; /* typelem value to pass to the output fn */ Oid typelem; /* typelem value to pass to the output fn */
bool typisvarlena; /* is it varlena (ie possibly toastable)? */
FmgrInfo finfo; /* Precomputed call info for typoutput */ FmgrInfo finfo; /* Precomputed call info for typoutput */
} PrinttupAttrInfo; } PrinttupAttrInfo;
@ -83,11 +81,11 @@ typedef struct
* ---------------- * ----------------
*/ */
DestReceiver * DestReceiver *
printtup_create_DR() printtup_create_DR(bool isBinary)
{ {
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup)); DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
self->pub.receiveTuple = printtup; self->pub.receiveTuple = isBinary ? printtup_internal : printtup;
self->pub.setup = printtup_setup; self->pub.setup = printtup_setup;
self->pub.cleanup = printtup_cleanup; self->pub.cleanup = printtup_cleanup;
@ -132,8 +130,9 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
{ {
PrinttupAttrInfo *thisState = myState->myinfo + i; PrinttupAttrInfo *thisState = myState->myinfo + i;
if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid, if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
&thisState->typoutput, &thisState->typelem)) &thisState->typoutput, &thisState->typelem,
&thisState->typisvarlena))
fmgr_info(thisState->typoutput, &thisState->finfo); fmgr_info(thisState->typoutput, &thisState->finfo);
} }
} }
@ -147,17 +146,14 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{ {
DR_printtup *myState = (DR_printtup *) self; DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf; StringInfoData buf;
int natts = tuple->t_data->t_natts;
int i, int i,
j, j,
k; k;
char *outputstr;
Datum attr;
bool isnull;
/* Set or update my derived attribute info, if needed */ /* Set or update my derived attribute info, if needed */
if (myState->attrinfo != typeinfo || if (myState->attrinfo != typeinfo || myState->nattrs != natts)
myState->nattrs != tuple->t_data->t_natts) printtup_prepare_info(myState, typeinfo, natts);
printtup_prepare_info(myState, typeinfo, tuple->t_data->t_natts);
/* ---------------- /* ----------------
* tell the frontend to expect new tuple data (in ASCII style) * tell the frontend to expect new tuple data (in ASCII style)
@ -172,7 +168,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
*/ */
j = 0; j = 0;
k = 1 << 7; k = 1 << 7;
for (i = 0; i < tuple->t_data->t_natts; ++i) for (i = 0; i < natts; ++i)
{ {
if (!heap_attisnull(tuple, i + 1)) if (!heap_attisnull(tuple, i + 1))
j |= k; /* set bit if not null */ j |= k; /* set bit if not null */
@ -191,20 +187,38 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
* send the attributes of this tuple * send the attributes of this tuple
* ---------------- * ----------------
*/ */
for (i = 0; i < tuple->t_data->t_natts; ++i) for (i = 0; i < natts; ++i)
{ {
PrinttupAttrInfo *thisState = myState->myinfo + i; PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr,
attr;
bool isnull;
char *outputstr;
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull); origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
if (isnull) if (isnull)
continue; continue;
if (OidIsValid(thisState->typoutput)) if (OidIsValid(thisState->typoutput))
{ {
/*
* 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 = DatumGetCString(FunctionCall3(&thisState->finfo, outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
attr, attr,
ObjectIdGetDatum(thisState->typelem), ObjectIdGetDatum(thisState->typelem),
Int32GetDatum(typeinfo->attrs[i]->atttypmod))); Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
pq_sendcountedtext(&buf, outputstr, strlen(outputstr)); pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
/* Clean up detoasted copy, if any */
if (attr != origattr)
pfree(DatumGetPointer(attr));
pfree(outputstr); pfree(outputstr);
} }
else else
@ -276,26 +290,43 @@ showatts(char *name, TupleDesc tupleDesc)
void void
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{ {
int natts = tuple->t_data->t_natts;
int i; int i;
Datum attr; Datum origattr,
attr;
char *value; char *value;
bool isnull; bool isnull;
Oid typoutput, Oid typoutput,
typelem; typelem;
bool typisvarlena;
for (i = 0; i < tuple->t_data->t_natts; ++i) for (i = 0; i < natts; ++i)
{ {
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull); origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
if (isnull) if (isnull)
continue; continue;
if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid, if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
&typoutput, &typelem)) &typoutput, &typelem, &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 = DatumGetCString(OidFunctionCall3(typoutput, value = DatumGetCString(OidFunctionCall3(typoutput,
attr, attr,
ObjectIdGetDatum(typelem), ObjectIdGetDatum(typelem),
Int32GetDatum(typeinfo->attrs[i]->atttypmod))); Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
printatt((unsigned) i + 1, typeinfo->attrs[i], value); printatt((unsigned) i + 1, typeinfo->attrs[i], value);
/* Clean up detoasted copy, if any */
if (attr != origattr)
pfree(DatumGetPointer(attr));
pfree(value); pfree(value);
} }
} }
@ -307,19 +338,22 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
* We use a different data prefix, e.g. 'B' instead of 'D' to * We use a different data prefix, e.g. 'B' instead of 'D' to
* indicate a tuple in internal (binary) form. * indicate a tuple in internal (binary) form.
* *
* This is same as printtup, except we don't use the typout func, * This is largely same as printtup, except we don't use the typout func.
* and therefore have no need for persistent state.
* ---------------- * ----------------
*/ */
void static void
printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{ {
DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf; StringInfoData buf;
int natts = tuple->t_data->t_natts;
int i, int i,
j, j,
k; k;
Datum attr;
bool isnull; /* Set or update my derived attribute info, if needed */
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts);
/* ---------------- /* ----------------
* tell the frontend to expect new tuple data (in binary style) * tell the frontend to expect new tuple data (in binary style)
@ -334,7 +368,7 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
*/ */
j = 0; j = 0;
k = 1 << 7; k = 1 << 7;
for (i = 0; i < tuple->t_data->t_natts; ++i) for (i = 0; i < natts; ++i)
{ {
if (!heap_attisnull(tuple, i + 1)) if (!heap_attisnull(tuple, i + 1))
j |= k; /* set bit if not null */ j |= k; /* set bit if not null */
@ -354,19 +388,28 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
* ---------------- * ----------------
*/ */
#ifdef IPORTAL_DEBUG #ifdef IPORTAL_DEBUG
fprintf(stderr, "sending tuple with %d atts\n", tuple->t_data->t_natts); fprintf(stderr, "sending tuple with %d atts\n", natts);
#endif #endif
for (i = 0; i < tuple->t_data->t_natts; ++i)
{
int32 len = typeinfo->attrs[i]->attlen;
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull); for (i = 0; i < natts; ++i)
if (!isnull)
{ {
/* # of bytes, and opaque data */ PrinttupAttrInfo *thisState = myState->myinfo + i;
if (len == -1) Datum origattr,
attr;
bool isnull;
int32 len;
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
if (isnull)
continue;
/* send # of bytes, and opaque data */
if (thisState->typisvarlena)
{ {
/* variable length, assume a varlena structure */ /*
* If we have a toasted datum, must detoast before sending.
*/
attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
len = VARSIZE(attr) - VARHDRSZ; len = VARSIZE(attr) - VARHDRSZ;
pq_sendint(&buf, len, VARHDRSZ); pq_sendint(&buf, len, VARHDRSZ);
@ -376,21 +419,27 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
{ {
char *d = VARDATA(attr); char *d = VARDATA(attr);
fprintf(stderr, "length %d data %x%x%x%x\n", fprintf(stderr, "length %d data %x %x %x %x\n",
len, *d, *(d + 1), *(d + 2), *(d + 3)); len, *d, *(d + 1), *(d + 2), *(d + 3));
} }
#endif #endif
/* Clean up detoasted copy, if any */
if (attr != origattr)
pfree(DatumGetPointer(attr));
} }
else else
{ {
/* fixed size */ /* fixed size */
attr = origattr;
len = typeinfo->attrs[i]->attlen;
pq_sendint(&buf, len, sizeof(int32));
if (typeinfo->attrs[i]->attbyval) if (typeinfo->attrs[i]->attbyval)
{ {
int8 i8; int8 i8;
int16 i16; int16 i16;
int32 i32; int32 i32;
pq_sendint(&buf, len, sizeof(int32));
switch (len) switch (len)
{ {
case sizeof(int8): case sizeof(int8):
@ -405,6 +454,9 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
i32 = DatumGetInt32(attr); i32 = DatumGetInt32(attr);
pq_sendbytes(&buf, (char *) &i32, len); pq_sendbytes(&buf, (char *) &i32, len);
break; break;
default:
elog(ERROR, "printtup_internal: unexpected typlen");
break;
} }
#ifdef IPORTAL_DEBUG #ifdef IPORTAL_DEBUG
fprintf(stderr, "byval length %d data %d\n", len, attr); fprintf(stderr, "byval length %d data %d\n", len, attr);
@ -412,16 +464,14 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
} }
else else
{ {
pq_sendint(&buf, len, sizeof(int32));
pq_sendbytes(&buf, DatumGetPointer(attr), len); pq_sendbytes(&buf, DatumGetPointer(attr), len);
#ifdef IPORTAL_DEBUG #ifdef IPORTAL_DEBUG
fprintf(stderr, "byref length %d data %x\n", len, fprintf(stderr, "byref length %d data %p\n", len,
DatumGetPointer(attr)); DatumGetPointer(attr));
#endif #endif
} }
} }
} }
}
pq_endmessage(&buf); pq_endmessage(&buf);
} }

View File

@ -3,7 +3,7 @@
* spi.c * spi.c
* Server Programming Interface * Server Programming Interface
* *
* $Id: spi.c,v 1.49 2000/11/16 22:30:22 tgl Exp $ * $Id: spi.c,v 1.50 2000/12/01 22:10:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -397,10 +397,13 @@ SPI_fname(TupleDesc tupdesc, int fnumber)
char * char *
SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
{ {
Datum val; Datum origval,
val,
result;
bool isnull; bool isnull;
Oid foutoid, Oid foutoid,
typelem; typelem;
bool typisvarlena;
SPI_result = 0; SPI_result = 0;
if (tuple->t_data->t_natts < fnumber || fnumber <= 0) if (tuple->t_data->t_natts < fnumber || fnumber <= 0)
@ -409,20 +412,35 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
return NULL; return NULL;
} }
val = heap_getattr(tuple, fnumber, tupdesc, &isnull); origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
if (isnull) if (isnull)
return NULL; return NULL;
if (!getTypeOutAndElem((Oid) tupdesc->attrs[fnumber - 1]->atttypid, if (!getTypeOutputInfo(tupdesc->attrs[fnumber - 1]->atttypid,
&foutoid, &typelem)) &foutoid, &typelem, &typisvarlena))
{ {
SPI_result = SPI_ERROR_NOOUTFUNC; SPI_result = SPI_ERROR_NOOUTFUNC;
return NULL; return NULL;
} }
return DatumGetCString(OidFunctionCall3(foutoid, /*
* If we have a toasted datum, forcibly detoast it here to avoid memory
* leakage inside the type's output routine.
*/
if (typisvarlena)
val = PointerGetDatum(PG_DETOAST_DATUM(origval));
else
val = origval;
result = OidFunctionCall3(foutoid,
val, val,
ObjectIdGetDatum(typelem), ObjectIdGetDatum(typelem),
Int32GetDatum(tupdesc->attrs[fnumber - 1]->atttypmod))); Int32GetDatum(tupdesc->attrs[fnumber - 1]->atttypmod));
/* Clean up detoasted copy, if any */
if (val != origval)
pfree(DatumGetPointer(val));
return DatumGetCString(result);
} }
Datum Datum

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.40 2000/10/22 22:14:55 petere Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.41 2000/12/01 22:10:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -69,9 +69,6 @@ donothingCleanup(DestReceiver *self)
static DestReceiver donothingDR = { static DestReceiver donothingDR = {
donothingReceive, donothingSetup, donothingCleanup donothingReceive, donothingSetup, donothingCleanup
}; };
static DestReceiver printtup_internalDR = {
printtup_internal, donothingSetup, donothingCleanup
};
static DestReceiver debugtupDR = { static DestReceiver debugtupDR = {
debugtup, donothingSetup, donothingCleanup debugtup, donothingSetup, donothingCleanup
}; };
@ -180,11 +177,10 @@ DestToFunction(CommandDest dest)
switch (dest) switch (dest)
{ {
case Remote: case Remote:
/* printtup wants a dynamically allocated DestReceiver */ return printtup_create_DR(false);
return printtup_create_DR();
case RemoteInternal: case RemoteInternal:
return &printtup_internalDR; return printtup_create_DR(true);
case Debug: case Debug:
return &debugtupDR; return &debugtupDR;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: printtup.h,v 1.12 2000/01/26 05:57:50 momjian Exp $ * $Id: printtup.h,v 1.13 2000/12/01 22:10:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -16,17 +16,17 @@
#include "tcop/dest.h" #include "tcop/dest.h"
extern DestReceiver *printtup_create_DR(void); extern DestReceiver *printtup_create_DR(bool isBinary);
extern void showatts(char *name, TupleDesc attinfo); extern void showatts(char *name, TupleDesc attinfo);
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo, extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
DestReceiver *self); DestReceiver *self);
extern void printtup_internal(HeapTuple tuple, TupleDesc typeinfo,
DestReceiver *self);
/* XXX this one is really in executor/spi.c */ /* XXX this one is really in executor/spi.c */
extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc, extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc,
DestReceiver *self); DestReceiver *self);
extern int getTypeOutAndElem(Oid type, Oid *typOutput, Oid *typElem); extern bool getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
bool *typIsVarlena);
#endif /* PRINTTUP_H */ #endif /* PRINTTUP_H */