Skip to content

Commit 217d156

Browse files
committed
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...
1 parent f5371fe commit 217d156

File tree

4 files changed

+172
-108
lines changed

4 files changed

+172
-108
lines changed

src/backend/access/common/printtup.c

Lines changed: 135 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.54 2000/11/16 22:30:15 tgl Exp $
12+
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.55 2000/12/01 22:10:31 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
16-
17-
1816
#include "postgres.h"
1917

2018
#include "access/heapam.h"
@@ -25,6 +23,7 @@
2523

2624
static void printtup_setup(DestReceiver *self, TupleDesc typeinfo);
2725
static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
26+
static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
2827
static void printtup_cleanup(DestReceiver *self);
2928

3029
/* ----------------------------------------------------------------
@@ -33,15 +32,12 @@ static void printtup_cleanup(DestReceiver *self);
3332
*/
3433

3534
/* ----------------
36-
* getTypeOutAndElem -- get both typoutput and typelem for a type
37-
*
38-
* We used to fetch these with two separate function calls,
39-
* typtoout() and gettypelem(), which each called SearchSysCache.
40-
* This way takes half the time.
35+
* getTypeOutputInfo -- get info needed for printing values of a type
4136
* ----------------
4237
*/
43-
int
44-
getTypeOutAndElem(Oid type, Oid *typOutput, Oid *typElem)
38+
bool
39+
getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
40+
bool *typIsVarlena)
4541
{
4642
HeapTuple typeTuple;
4743
Form_pg_type pt;
@@ -50,11 +46,12 @@ getTypeOutAndElem(Oid type, Oid *typOutput, Oid *typElem)
5046
ObjectIdGetDatum(type),
5147
0, 0, 0);
5248
if (!HeapTupleIsValid(typeTuple))
53-
elog(ERROR, "getTypeOutAndElem: Cache lookup of type %u failed", type);
49+
elog(ERROR, "getTypeOutputInfo: Cache lookup of type %u failed", type);
5450
pt = (Form_pg_type) GETSTRUCT(typeTuple);
5551

5652
*typOutput = pt->typoutput;
5753
*typElem = pt->typelem;
54+
*typIsVarlena = (! pt->typbyval) && (pt->typlen == -1);
5855
ReleaseSysCache(typeTuple);
5956
return OidIsValid(*typOutput);
6057
}
@@ -67,6 +64,7 @@ typedef struct
6764
{ /* Per-attribute information */
6865
Oid typoutput; /* Oid for the attribute's type output fn */
6966
Oid typelem; /* typelem value to pass to the output fn */
67+
bool typisvarlena; /* is it varlena (ie possibly toastable)? */
7068
FmgrInfo finfo; /* Precomputed call info for typoutput */
7169
} PrinttupAttrInfo;
7270

@@ -83,11 +81,11 @@ typedef struct
8381
* ----------------
8482
*/
8583
DestReceiver *
86-
printtup_create_DR()
84+
printtup_create_DR(bool isBinary)
8785
{
8886
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
8987

90-
self->pub.receiveTuple = printtup;
88+
self->pub.receiveTuple = isBinary ? printtup_internal : printtup;
9189
self->pub.setup = printtup_setup;
9290
self->pub.cleanup = printtup_cleanup;
9391

@@ -132,8 +130,9 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
132130
{
133131
PrinttupAttrInfo *thisState = myState->myinfo + i;
134132

135-
if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
136-
&thisState->typoutput, &thisState->typelem))
133+
if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
134+
&thisState->typoutput, &thisState->typelem,
135+
&thisState->typisvarlena))
137136
fmgr_info(thisState->typoutput, &thisState->finfo);
138137
}
139138
}
@@ -147,17 +146,14 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
147146
{
148147
DR_printtup *myState = (DR_printtup *) self;
149148
StringInfoData buf;
149+
int natts = tuple->t_data->t_natts;
150150
int i,
151151
j,
152152
k;
153-
char *outputstr;
154-
Datum attr;
155-
bool isnull;
156153

157154
/* Set or update my derived attribute info, if needed */
158-
if (myState->attrinfo != typeinfo ||
159-
myState->nattrs != tuple->t_data->t_natts)
160-
printtup_prepare_info(myState, typeinfo, tuple->t_data->t_natts);
155+
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
156+
printtup_prepare_info(myState, typeinfo, natts);
161157

162158
/* ----------------
163159
* tell the frontend to expect new tuple data (in ASCII style)
@@ -172,7 +168,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
172168
*/
173169
j = 0;
174170
k = 1 << 7;
175-
for (i = 0; i < tuple->t_data->t_natts; ++i)
171+
for (i = 0; i < natts; ++i)
176172
{
177173
if (!heap_attisnull(tuple, i + 1))
178174
j |= k; /* set bit if not null */
@@ -191,20 +187,38 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
191187
* send the attributes of this tuple
192188
* ----------------
193189
*/
194-
for (i = 0; i < tuple->t_data->t_natts; ++i)
190+
for (i = 0; i < natts; ++i)
195191
{
196192
PrinttupAttrInfo *thisState = myState->myinfo + i;
193+
Datum origattr,
194+
attr;
195+
bool isnull;
196+
char *outputstr;
197197

198-
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
198+
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
199199
if (isnull)
200200
continue;
201201
if (OidIsValid(thisState->typoutput))
202202
{
203+
/*
204+
* If we have a toasted datum, forcibly detoast it here to avoid
205+
* memory leakage inside the type's output routine.
206+
*/
207+
if (thisState->typisvarlena)
208+
attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
209+
else
210+
attr = origattr;
211+
203212
outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
204213
attr,
205214
ObjectIdGetDatum(thisState->typelem),
206215
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
216+
207217
pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
218+
219+
/* Clean up detoasted copy, if any */
220+
if (attr != origattr)
221+
pfree(DatumGetPointer(attr));
208222
pfree(outputstr);
209223
}
210224
else
@@ -276,26 +290,43 @@ showatts(char *name, TupleDesc tupleDesc)
276290
void
277291
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
278292
{
293+
int natts = tuple->t_data->t_natts;
279294
int i;
280-
Datum attr;
295+
Datum origattr,
296+
attr;
281297
char *value;
282298
bool isnull;
283299
Oid typoutput,
284300
typelem;
301+
bool typisvarlena;
285302

286-
for (i = 0; i < tuple->t_data->t_natts; ++i)
303+
for (i = 0; i < natts; ++i)
287304
{
288-
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
305+
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
289306
if (isnull)
290307
continue;
291-
if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
292-
&typoutput, &typelem))
308+
if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
309+
&typoutput, &typelem, &typisvarlena))
293310
{
311+
/*
312+
* If we have a toasted datum, forcibly detoast it here to avoid
313+
* memory leakage inside the type's output routine.
314+
*/
315+
if (typisvarlena)
316+
attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
317+
else
318+
attr = origattr;
319+
294320
value = DatumGetCString(OidFunctionCall3(typoutput,
295321
attr,
296322
ObjectIdGetDatum(typelem),
297323
Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
324+
298325
printatt((unsigned) i + 1, typeinfo->attrs[i], value);
326+
327+
/* Clean up detoasted copy, if any */
328+
if (attr != origattr)
329+
pfree(DatumGetPointer(attr));
299330
pfree(value);
300331
}
301332
}
@@ -307,19 +338,22 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
307338
* We use a different data prefix, e.g. 'B' instead of 'D' to
308339
* indicate a tuple in internal (binary) form.
309340
*
310-
* This is same as printtup, except we don't use the typout func,
311-
* and therefore have no need for persistent state.
341+
* This is largely same as printtup, except we don't use the typout func.
312342
* ----------------
313343
*/
314-
void
344+
static void
315345
printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
316346
{
347+
DR_printtup *myState = (DR_printtup *) self;
317348
StringInfoData buf;
349+
int natts = tuple->t_data->t_natts;
318350
int i,
319351
j,
320352
k;
321-
Datum attr;
322-
bool isnull;
353+
354+
/* Set or update my derived attribute info, if needed */
355+
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
356+
printtup_prepare_info(myState, typeinfo, natts);
323357

324358
/* ----------------
325359
* tell the frontend to expect new tuple data (in binary style)
@@ -334,7 +368,7 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
334368
*/
335369
j = 0;
336370
k = 1 << 7;
337-
for (i = 0; i < tuple->t_data->t_natts; ++i)
371+
for (i = 0; i < natts; ++i)
338372
{
339373
if (!heap_attisnull(tuple, i + 1))
340374
j |= k; /* set bit if not null */
@@ -354,71 +388,87 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
354388
* ----------------
355389
*/
356390
#ifdef IPORTAL_DEBUG
357-
fprintf(stderr, "sending tuple with %d atts\n", tuple->t_data->t_natts);
391+
fprintf(stderr, "sending tuple with %d atts\n", natts);
358392
#endif
359-
for (i = 0; i < tuple->t_data->t_natts; ++i)
393+
394+
for (i = 0; i < natts; ++i)
360395
{
361-
int32 len = typeinfo->attrs[i]->attlen;
396+
PrinttupAttrInfo *thisState = myState->myinfo + i;
397+
Datum origattr,
398+
attr;
399+
bool isnull;
400+
int32 len;
362401

363-
attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
364-
if (!isnull)
402+
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
403+
if (isnull)
404+
continue;
405+
/* send # of bytes, and opaque data */
406+
if (thisState->typisvarlena)
365407
{
366-
/* # of bytes, and opaque data */
367-
if (len == -1)
368-
{
369-
/* variable length, assume a varlena structure */
370-
len = VARSIZE(attr) - VARHDRSZ;
408+
/*
409+
* If we have a toasted datum, must detoast before sending.
410+
*/
411+
attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
412+
413+
len = VARSIZE(attr) - VARHDRSZ;
371414

372-
pq_sendint(&buf, len, VARHDRSZ);
373-
pq_sendbytes(&buf, VARDATA(attr), len);
415+
pq_sendint(&buf, len, VARHDRSZ);
416+
pq_sendbytes(&buf, VARDATA(attr), len);
374417

375418
#ifdef IPORTAL_DEBUG
376-
{
377-
char *d = VARDATA(attr);
419+
{
420+
char *d = VARDATA(attr);
378421

379-
fprintf(stderr, "length %d data %x%x%x%x\n",
380-
len, *d, *(d + 1), *(d + 2), *(d + 3));
381-
}
382-
#endif
422+
fprintf(stderr, "length %d data %x %x %x %x\n",
423+
len, *d, *(d + 1), *(d + 2), *(d + 3));
383424
}
384-
else
425+
#endif
426+
427+
/* Clean up detoasted copy, if any */
428+
if (attr != origattr)
429+
pfree(DatumGetPointer(attr));
430+
}
431+
else
432+
{
433+
/* fixed size */
434+
attr = origattr;
435+
len = typeinfo->attrs[i]->attlen;
436+
pq_sendint(&buf, len, sizeof(int32));
437+
if (typeinfo->attrs[i]->attbyval)
385438
{
386-
/* fixed size */
387-
if (typeinfo->attrs[i]->attbyval)
439+
int8 i8;
440+
int16 i16;
441+
int32 i32;
442+
443+
switch (len)
388444
{
389-
int8 i8;
390-
int16 i16;
391-
int32 i32;
392-
393-
pq_sendint(&buf, len, sizeof(int32));
394-
switch (len)
395-
{
396-
case sizeof(int8):
397-
i8 = DatumGetChar(attr);
398-
pq_sendbytes(&buf, (char *) &i8, len);
399-
break;
400-
case sizeof(int16):
401-
i16 = DatumGetInt16(attr);
402-
pq_sendbytes(&buf, (char *) &i16, len);
403-
break;
404-
case sizeof(int32):
405-
i32 = DatumGetInt32(attr);
406-
pq_sendbytes(&buf, (char *) &i32, len);
407-
break;
408-
}
445+
case sizeof(int8):
446+
i8 = DatumGetChar(attr);
447+
pq_sendbytes(&buf, (char *) &i8, len);
448+
break;
449+
case sizeof(int16):
450+
i16 = DatumGetInt16(attr);
451+
pq_sendbytes(&buf, (char *) &i16, len);
452+
break;
453+
case sizeof(int32):
454+
i32 = DatumGetInt32(attr);
455+
pq_sendbytes(&buf, (char *) &i32, len);
456+
break;
457+
default:
458+
elog(ERROR, "printtup_internal: unexpected typlen");
459+
break;
460+
}
409461
#ifdef IPORTAL_DEBUG
410-
fprintf(stderr, "byval length %d data %d\n", len, attr);
462+
fprintf(stderr, "byval length %d data %d\n", len, attr);
411463
#endif
412-
}
413-
else
414-
{
415-
pq_sendint(&buf, len, sizeof(int32));
416-
pq_sendbytes(&buf, DatumGetPointer(attr), len);
464+
}
465+
else
466+
{
467+
pq_sendbytes(&buf, DatumGetPointer(attr), len);
417468
#ifdef IPORTAL_DEBUG
418-
fprintf(stderr, "byref length %d data %x\n", len,
419-
DatumGetPointer(attr));
469+
fprintf(stderr, "byref length %d data %p\n", len,
470+
DatumGetPointer(attr));
420471
#endif
421-
}
422472
}
423473
}
424474
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy