diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index ccf3071b50..b05902068e 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -9,12 +9,10 @@ * * * 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 "access/heapam.h" @@ -25,6 +23,7 @@ static void printtup_setup(DestReceiver *self, TupleDesc typeinfo); 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); /* ---------------------------------------------------------------- @@ -33,15 +32,12 @@ static void printtup_cleanup(DestReceiver *self); */ /* ---------------- - * getTypeOutAndElem -- get both typoutput and typelem for 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. + * getTypeOutputInfo -- get info needed for printing values of a type * ---------------- */ -int -getTypeOutAndElem(Oid type, Oid *typOutput, Oid *typElem) +bool +getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem, + bool *typIsVarlena) { HeapTuple typeTuple; Form_pg_type pt; @@ -50,11 +46,12 @@ getTypeOutAndElem(Oid type, Oid *typOutput, Oid *typElem) ObjectIdGetDatum(type), 0, 0, 0); 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); *typOutput = pt->typoutput; *typElem = pt->typelem; + *typIsVarlena = (! pt->typbyval) && (pt->typlen == -1); ReleaseSysCache(typeTuple); return OidIsValid(*typOutput); } @@ -67,6 +64,7 @@ typedef struct { /* Per-attribute information */ Oid typoutput; /* Oid for the attribute's type 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 */ } PrinttupAttrInfo; @@ -83,11 +81,11 @@ typedef struct * ---------------- */ DestReceiver * -printtup_create_DR() +printtup_create_DR(bool isBinary) { 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.cleanup = printtup_cleanup; @@ -132,8 +130,9 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) { PrinttupAttrInfo *thisState = myState->myinfo + i; - if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid, - &thisState->typoutput, &thisState->typelem)) + if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid, + &thisState->typoutput, &thisState->typelem, + &thisState->typisvarlena)) fmgr_info(thisState->typoutput, &thisState->finfo); } } @@ -147,17 +146,14 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) { DR_printtup *myState = (DR_printtup *) self; StringInfoData buf; + int natts = tuple->t_data->t_natts; int i, j, k; - char *outputstr; - Datum attr; - bool isnull; /* Set or update my derived attribute info, if needed */ - if (myState->attrinfo != typeinfo || - myState->nattrs != tuple->t_data->t_natts) - printtup_prepare_info(myState, typeinfo, tuple->t_data->t_natts); + if (myState->attrinfo != typeinfo || myState->nattrs != natts) + printtup_prepare_info(myState, typeinfo, natts); /* ---------------- * tell the frontend to expect new tuple data (in ASCII style) @@ -172,7 +168,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) */ j = 0; 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)) j |= k; /* set bit if not null */ @@ -191,20 +187,38 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) * 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; + 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) continue; 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, attr, ObjectIdGetDatum(thisState->typelem), Int32GetDatum(typeinfo->attrs[i]->atttypmod))); + pq_sendcountedtext(&buf, outputstr, strlen(outputstr)); + + /* Clean up detoasted copy, if any */ + if (attr != origattr) + pfree(DatumGetPointer(attr)); pfree(outputstr); } else @@ -276,26 +290,43 @@ showatts(char *name, TupleDesc tupleDesc) void debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) { + int natts = tuple->t_data->t_natts; int i; - Datum attr; + Datum origattr, + attr; char *value; bool isnull; Oid typoutput, 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) continue; - if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid, - &typoutput, &typelem)) + if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid, + &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, attr, ObjectIdGetDatum(typelem), Int32GetDatum(typeinfo->attrs[i]->atttypmod))); + printatt((unsigned) i + 1, typeinfo->attrs[i], value); + + /* Clean up detoasted copy, if any */ + if (attr != origattr) + pfree(DatumGetPointer(attr)); 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 * indicate a tuple in internal (binary) form. * - * This is same as printtup, except we don't use the typout func, - * and therefore have no need for persistent state. + * This is largely same as printtup, except we don't use the typout func. * ---------------- */ -void +static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) { + DR_printtup *myState = (DR_printtup *) self; StringInfoData buf; + int natts = tuple->t_data->t_natts; int i, j, 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) @@ -334,7 +368,7 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) */ j = 0; 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)) j |= k; /* set bit if not null */ @@ -354,71 +388,87 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) * ---------------- */ #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 - for (i = 0; i < tuple->t_data->t_natts; ++i) + + for (i = 0; i < natts; ++i) { - int32 len = typeinfo->attrs[i]->attlen; + PrinttupAttrInfo *thisState = myState->myinfo + i; + Datum origattr, + attr; + bool isnull; + int32 len; - attr = heap_getattr(tuple, i + 1, typeinfo, &isnull); - if (!isnull) + origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull); + if (isnull) + continue; + /* send # of bytes, and opaque data */ + if (thisState->typisvarlena) { - /* # of bytes, and opaque data */ - if (len == -1) - { - /* variable length, assume a varlena structure */ - len = VARSIZE(attr) - VARHDRSZ; + /* + * If we have a toasted datum, must detoast before sending. + */ + attr = PointerGetDatum(PG_DETOAST_DATUM(origattr)); - pq_sendint(&buf, len, VARHDRSZ); - pq_sendbytes(&buf, VARDATA(attr), len); + len = VARSIZE(attr) - VARHDRSZ; + + pq_sendint(&buf, len, VARHDRSZ); + pq_sendbytes(&buf, VARDATA(attr), len); #ifdef IPORTAL_DEBUG - { - char *d = VARDATA(attr); + { + char *d = VARDATA(attr); - fprintf(stderr, "length %d data %x%x%x%x\n", - len, *d, *(d + 1), *(d + 2), *(d + 3)); + fprintf(stderr, "length %d data %x %x %x %x\n", + len, *d, *(d + 1), *(d + 2), *(d + 3)); + } +#endif + + /* Clean up detoasted copy, if any */ + if (attr != origattr) + pfree(DatumGetPointer(attr)); + } + else + { + /* fixed size */ + attr = origattr; + len = typeinfo->attrs[i]->attlen; + pq_sendint(&buf, len, sizeof(int32)); + if (typeinfo->attrs[i]->attbyval) + { + int8 i8; + int16 i16; + int32 i32; + + switch (len) + { + case sizeof(int8): + i8 = DatumGetChar(attr); + pq_sendbytes(&buf, (char *) &i8, len); + break; + case sizeof(int16): + i16 = DatumGetInt16(attr); + pq_sendbytes(&buf, (char *) &i16, len); + break; + case sizeof(int32): + i32 = DatumGetInt32(attr); + pq_sendbytes(&buf, (char *) &i32, len); + break; + default: + elog(ERROR, "printtup_internal: unexpected typlen"); + break; } +#ifdef IPORTAL_DEBUG + fprintf(stderr, "byval length %d data %d\n", len, attr); #endif } else { - /* fixed size */ - if (typeinfo->attrs[i]->attbyval) - { - int8 i8; - int16 i16; - int32 i32; - - pq_sendint(&buf, len, sizeof(int32)); - switch (len) - { - case sizeof(int8): - i8 = DatumGetChar(attr); - pq_sendbytes(&buf, (char *) &i8, len); - break; - case sizeof(int16): - i16 = DatumGetInt16(attr); - pq_sendbytes(&buf, (char *) &i16, len); - break; - case sizeof(int32): - i32 = DatumGetInt32(attr); - pq_sendbytes(&buf, (char *) &i32, len); - break; - } + pq_sendbytes(&buf, DatumGetPointer(attr), len); #ifdef IPORTAL_DEBUG - fprintf(stderr, "byval length %d data %d\n", len, attr); + fprintf(stderr, "byref length %d data %p\n", len, + DatumGetPointer(attr)); #endif - } - else - { - pq_sendint(&buf, len, sizeof(int32)); - pq_sendbytes(&buf, DatumGetPointer(attr), len); -#ifdef IPORTAL_DEBUG - fprintf(stderr, "byref length %d data %x\n", len, - DatumGetPointer(attr)); -#endif - } } } } diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 74a5dc4441..b309dc5022 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -3,7 +3,7 @@ * spi.c * 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 * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) { - Datum val; + Datum origval, + val, + result; bool isnull; Oid foutoid, typelem; + bool typisvarlena; SPI_result = 0; if (tuple->t_data->t_natts < fnumber || fnumber <= 0) @@ -409,20 +412,35 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) return NULL; } - val = heap_getattr(tuple, fnumber, tupdesc, &isnull); + origval = heap_getattr(tuple, fnumber, tupdesc, &isnull); if (isnull) return NULL; - if (!getTypeOutAndElem((Oid) tupdesc->attrs[fnumber - 1]->atttypid, - &foutoid, &typelem)) + if (!getTypeOutputInfo(tupdesc->attrs[fnumber - 1]->atttypid, + &foutoid, &typelem, &typisvarlena)) { SPI_result = SPI_ERROR_NOOUTFUNC; return NULL; } - return DatumGetCString(OidFunctionCall3(foutoid, - val, - ObjectIdGetDatum(typelem), - Int32GetDatum(tupdesc->attrs[fnumber - 1]->atttypmod))); + /* + * 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, + ObjectIdGetDatum(typelem), + Int32GetDatum(tupdesc->attrs[fnumber - 1]->atttypmod)); + + /* Clean up detoasted copy, if any */ + if (val != origval) + pfree(DatumGetPointer(val)); + + return DatumGetCString(result); } Datum diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c index ad3475ba8a..994c71c5a7 100644 --- a/src/backend/tcop/dest.c +++ b/src/backend/tcop/dest.c @@ -8,7 +8,7 @@ * * * 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 = { donothingReceive, donothingSetup, donothingCleanup }; -static DestReceiver printtup_internalDR = { - printtup_internal, donothingSetup, donothingCleanup -}; static DestReceiver debugtupDR = { debugtup, donothingSetup, donothingCleanup }; @@ -180,11 +177,10 @@ DestToFunction(CommandDest dest) switch (dest) { case Remote: - /* printtup wants a dynamically allocated DestReceiver */ - return printtup_create_DR(); + return printtup_create_DR(false); case RemoteInternal: - return &printtup_internalDR; + return printtup_create_DR(true); case Debug: return &debugtupDR; diff --git a/src/include/access/printtup.h b/src/include/access/printtup.h index bd5acd13e7..a70a9c35e9 100644 --- a/src/include/access/printtup.h +++ b/src/include/access/printtup.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * 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" -extern DestReceiver *printtup_create_DR(void); +extern DestReceiver *printtup_create_DR(bool isBinary); + extern void showatts(char *name, TupleDesc attinfo); extern void debugtup(HeapTuple tuple, TupleDesc typeinfo, - DestReceiver *self); -extern void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, - DestReceiver *self); + DestReceiver *self); /* XXX this one is really in executor/spi.c */ 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 */