20
20
#include "libpq/pqformat.h"
21
21
#include "tcop/pquery.h"
22
22
#include "utils/lsyscache.h"
23
+ #include "utils/memutils.h"
23
24
24
25
25
26
static void printtup_startup (DestReceiver * self , int operation ,
@@ -60,6 +61,7 @@ typedef struct
60
61
TupleDesc attrinfo ; /* The attr info we are set up for */
61
62
int nattrs ;
62
63
PrinttupAttrInfo * myinfo ; /* Cached info about each attr */
64
+ MemoryContext tmpcontext ; /* Memory context for per-row workspace */
63
65
} DR_printtup ;
64
66
65
67
/* ----------------
@@ -86,6 +88,7 @@ printtup_create_DR(CommandDest dest)
86
88
self -> attrinfo = NULL ;
87
89
self -> nattrs = 0 ;
88
90
self -> myinfo = NULL ;
91
+ self -> tmpcontext = NULL ;
89
92
90
93
return (DestReceiver * ) self ;
91
94
}
@@ -123,6 +126,18 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
123
126
DR_printtup * myState = (DR_printtup * ) self ;
124
127
Portal portal = myState -> portal ;
125
128
129
+ /*
130
+ * Create a temporary memory context that we can reset once per row to
131
+ * recover palloc'd memory. This avoids any problems with leaks inside
132
+ * datatype output routines, and should be faster than retail pfree's
133
+ * anyway.
134
+ */
135
+ myState -> tmpcontext = AllocSetContextCreate (CurrentMemoryContext ,
136
+ "printtup" ,
137
+ ALLOCSET_DEFAULT_MINSIZE ,
138
+ ALLOCSET_DEFAULT_INITSIZE ,
139
+ ALLOCSET_DEFAULT_MAXSIZE );
140
+
126
141
if (PG_PROTOCOL_MAJOR (FrontendProtocol ) < 3 )
127
142
{
128
143
/*
@@ -288,6 +303,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
288
303
{
289
304
TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
290
305
DR_printtup * myState = (DR_printtup * ) self ;
306
+ MemoryContext oldcontext ;
291
307
StringInfoData buf ;
292
308
int natts = typeinfo -> natts ;
293
309
int i ;
@@ -299,8 +315,11 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
299
315
/* Make sure the tuple is fully deconstructed */
300
316
slot_getallattrs (slot );
301
317
318
+ /* Switch into per-row context so we can recover memory below */
319
+ oldcontext = MemoryContextSwitchTo (myState -> tmpcontext );
320
+
302
321
/*
303
- * Prepare a DataRow message
322
+ * Prepare a DataRow message (note buffer is in per-row context)
304
323
*/
305
324
pq_beginmessage (& buf , 'D' );
306
325
@@ -312,32 +331,21 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
312
331
for (i = 0 ; i < natts ; ++ i )
313
332
{
314
333
PrinttupAttrInfo * thisState = myState -> myinfo + i ;
315
- Datum origattr = slot -> tts_values [i ],
316
- attr ;
334
+ Datum attr = slot -> tts_values [i ];
317
335
318
336
if (slot -> tts_isnull [i ])
319
337
{
320
338
pq_sendint (& buf , -1 , 4 );
321
339
continue ;
322
340
}
323
341
324
- /*
325
- * If we have a toasted datum, forcibly detoast it here to avoid
326
- * memory leakage inside the type's output routine.
327
- */
328
- if (thisState -> typisvarlena )
329
- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
330
- else
331
- attr = origattr ;
332
-
333
342
if (thisState -> format == 0 )
334
343
{
335
344
/* Text output */
336
345
char * outputstr ;
337
346
338
347
outputstr = OutputFunctionCall (& thisState -> finfo , attr );
339
348
pq_sendcountedtext (& buf , outputstr , strlen (outputstr ), false);
340
- pfree (outputstr );
341
349
}
342
350
else
343
351
{
@@ -348,15 +356,14 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
348
356
pq_sendint (& buf , VARSIZE (outputbytes ) - VARHDRSZ , 4 );
349
357
pq_sendbytes (& buf , VARDATA (outputbytes ),
350
358
VARSIZE (outputbytes ) - VARHDRSZ );
351
- pfree (outputbytes );
352
359
}
353
-
354
- /* Clean up detoasted copy, if any */
355
- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
356
- pfree (DatumGetPointer (attr ));
357
360
}
358
361
359
362
pq_endmessage (& buf );
363
+
364
+ /* Return to caller's context, and flush row's temporary memory */
365
+ MemoryContextSwitchTo (oldcontext );
366
+ MemoryContextReset (myState -> tmpcontext );
360
367
}
361
368
362
369
/* ----------------
@@ -368,6 +375,7 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
368
375
{
369
376
TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
370
377
DR_printtup * myState = (DR_printtup * ) self ;
378
+ MemoryContext oldcontext ;
371
379
StringInfoData buf ;
372
380
int natts = typeinfo -> natts ;
373
381
int i ,
@@ -381,6 +389,9 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
381
389
/* Make sure the tuple is fully deconstructed */
382
390
slot_getallattrs (slot );
383
391
392
+ /* Switch into per-row context so we can recover memory below */
393
+ oldcontext = MemoryContextSwitchTo (myState -> tmpcontext );
394
+
384
395
/*
385
396
* tell the frontend to expect new tuple data (in ASCII style)
386
397
*/
@@ -412,34 +423,23 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
412
423
for (i = 0 ; i < natts ; ++ i )
413
424
{
414
425
PrinttupAttrInfo * thisState = myState -> myinfo + i ;
415
- Datum origattr = slot -> tts_values [i ],
416
- attr ;
426
+ Datum attr = slot -> tts_values [i ];
417
427
char * outputstr ;
418
428
419
429
if (slot -> tts_isnull [i ])
420
430
continue ;
421
431
422
432
Assert (thisState -> format == 0 );
423
433
424
- /*
425
- * If we have a toasted datum, forcibly detoast it here to avoid
426
- * memory leakage inside the type's output routine.
427
- */
428
- if (thisState -> typisvarlena )
429
- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
430
- else
431
- attr = origattr ;
432
-
433
434
outputstr = OutputFunctionCall (& thisState -> finfo , attr );
434
435
pq_sendcountedtext (& buf , outputstr , strlen (outputstr ), true);
435
- pfree (outputstr );
436
-
437
- /* Clean up detoasted copy, if any */
438
- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
439
- pfree (DatumGetPointer (attr ));
440
436
}
441
437
442
438
pq_endmessage (& buf );
439
+
440
+ /* Return to caller's context, and flush row's temporary memory */
441
+ MemoryContextSwitchTo (oldcontext );
442
+ MemoryContextReset (myState -> tmpcontext );
443
443
}
444
444
445
445
/* ----------------
@@ -456,6 +456,10 @@ printtup_shutdown(DestReceiver *self)
456
456
myState -> myinfo = NULL ;
457
457
458
458
myState -> attrinfo = NULL ;
459
+
460
+ if (myState -> tmpcontext )
461
+ MemoryContextDelete (myState -> tmpcontext );
462
+ myState -> tmpcontext = NULL ;
459
463
}
460
464
461
465
/* ----------------
@@ -518,39 +522,23 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
518
522
TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
519
523
int natts = typeinfo -> natts ;
520
524
int i ;
521
- Datum origattr ,
522
- attr ;
525
+ Datum attr ;
523
526
char * value ;
524
527
bool isnull ;
525
528
Oid typoutput ;
526
529
bool typisvarlena ;
527
530
528
531
for (i = 0 ; i < natts ; ++ i )
529
532
{
530
- origattr = slot_getattr (slot , i + 1 , & isnull );
533
+ attr = slot_getattr (slot , i + 1 , & isnull );
531
534
if (isnull )
532
535
continue ;
533
536
getTypeOutputInfo (typeinfo -> attrs [i ]-> atttypid ,
534
537
& typoutput , & typisvarlena );
535
538
536
- /*
537
- * If we have a toasted datum, forcibly detoast it here to avoid
538
- * memory leakage inside the type's output routine.
539
- */
540
- if (typisvarlena )
541
- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
542
- else
543
- attr = origattr ;
544
-
545
539
value = OidOutputFunctionCall (typoutput , attr );
546
540
547
541
printatt ((unsigned ) i + 1 , typeinfo -> attrs [i ], value );
548
-
549
- pfree (value );
550
-
551
- /* Clean up detoasted copy, if any */
552
- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
553
- pfree (DatumGetPointer (attr ));
554
542
}
555
543
printf ("\t----\n" );
556
544
}
@@ -569,6 +557,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
569
557
{
570
558
TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
571
559
DR_printtup * myState = (DR_printtup * ) self ;
560
+ MemoryContext oldcontext ;
572
561
StringInfoData buf ;
573
562
int natts = typeinfo -> natts ;
574
563
int i ,
@@ -582,6 +571,9 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
582
571
/* Make sure the tuple is fully deconstructed */
583
572
slot_getallattrs (slot );
584
573
574
+ /* Switch into per-row context so we can recover memory below */
575
+ oldcontext = MemoryContextSwitchTo (myState -> tmpcontext );
576
+
585
577
/*
586
578
* tell the frontend to expect new tuple data (in binary style)
587
579
*/
@@ -613,35 +605,23 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
613
605
for (i = 0 ; i < natts ; ++ i )
614
606
{
615
607
PrinttupAttrInfo * thisState = myState -> myinfo + i ;
616
- Datum origattr = slot -> tts_values [i ],
617
- attr ;
608
+ Datum attr = slot -> tts_values [i ];
618
609
bytea * outputbytes ;
619
610
620
611
if (slot -> tts_isnull [i ])
621
612
continue ;
622
613
623
614
Assert (thisState -> format == 1 );
624
615
625
- /*
626
- * If we have a toasted datum, forcibly detoast it here to avoid
627
- * memory leakage inside the type's output routine.
628
- */
629
- if (thisState -> typisvarlena )
630
- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
631
- else
632
- attr = origattr ;
633
-
634
616
outputbytes = SendFunctionCall (& thisState -> finfo , attr );
635
- /* We assume the result will not have been toasted */
636
617
pq_sendint (& buf , VARSIZE (outputbytes ) - VARHDRSZ , 4 );
637
618
pq_sendbytes (& buf , VARDATA (outputbytes ),
638
619
VARSIZE (outputbytes ) - VARHDRSZ );
639
- pfree (outputbytes );
640
-
641
- /* Clean up detoasted copy, if any */
642
- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
643
- pfree (DatumGetPointer (attr ));
644
620
}
645
621
646
622
pq_endmessage (& buf );
623
+
624
+ /* Return to caller's context, and flush row's temporary memory */
625
+ MemoryContextSwitchTo (oldcontext );
626
+ MemoryContextReset (myState -> tmpcontext );
647
627
}
0 commit comments