21
21
#include "tcop/pquery.h"
22
22
#include "utils/lsyscache.h"
23
23
#include "utils/memdebug.h"
24
+ #include "utils/memutils.h"
24
25
25
26
26
27
static void printtup_startup (DestReceiver * self , int operation ,
@@ -61,6 +62,7 @@ typedef struct
61
62
TupleDesc attrinfo ; /* The attr info we are set up for */
62
63
int nattrs ;
63
64
PrinttupAttrInfo * myinfo ; /* Cached info about each attr */
65
+ MemoryContext tmpcontext ; /* Memory context for per-row workspace */
64
66
} DR_printtup ;
65
67
66
68
/* ----------------
@@ -87,6 +89,7 @@ printtup_create_DR(CommandDest dest)
87
89
self -> attrinfo = NULL ;
88
90
self -> nattrs = 0 ;
89
91
self -> myinfo = NULL ;
92
+ self -> tmpcontext = NULL ;
90
93
91
94
return (DestReceiver * ) self ;
92
95
}
@@ -124,6 +127,18 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
124
127
DR_printtup * myState = (DR_printtup * ) self ;
125
128
Portal portal = myState -> portal ;
126
129
130
+ /*
131
+ * Create a temporary memory context that we can reset once per row to
132
+ * recover palloc'd memory. This avoids any problems with leaks inside
133
+ * datatype output routines, and should be faster than retail pfree's
134
+ * anyway.
135
+ */
136
+ myState -> tmpcontext = AllocSetContextCreate (CurrentMemoryContext ,
137
+ "printtup" ,
138
+ ALLOCSET_DEFAULT_MINSIZE ,
139
+ ALLOCSET_DEFAULT_INITSIZE ,
140
+ ALLOCSET_DEFAULT_MAXSIZE );
141
+
127
142
if (PG_PROTOCOL_MAJOR (FrontendProtocol ) < 3 )
128
143
{
129
144
/*
@@ -289,6 +304,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
289
304
{
290
305
TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
291
306
DR_printtup * myState = (DR_printtup * ) self ;
307
+ MemoryContext oldcontext ;
292
308
StringInfoData buf ;
293
309
int natts = typeinfo -> natts ;
294
310
int i ;
@@ -300,8 +316,11 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
300
316
/* Make sure the tuple is fully deconstructed */
301
317
slot_getallattrs (slot );
302
318
319
+ /* Switch into per-row context so we can recover memory below */
320
+ oldcontext = MemoryContextSwitchTo (myState -> tmpcontext );
321
+
303
322
/*
304
- * Prepare a DataRow message
323
+ * Prepare a DataRow message (note buffer is in per-row context)
305
324
*/
306
325
pq_beginmessage (& buf , 'D' );
307
326
@@ -313,8 +332,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
313
332
for (i = 0 ; i < natts ; ++ i )
314
333
{
315
334
PrinttupAttrInfo * thisState = myState -> myinfo + i ;
316
- Datum origattr = slot -> tts_values [i ],
317
- attr ;
335
+ Datum attr = slot -> tts_values [i ];
318
336
319
337
if (slot -> tts_isnull [i ])
320
338
{
@@ -323,30 +341,15 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
323
341
}
324
342
325
343
/*
326
- * If we have a toasted datum, forcibly detoast it here to avoid
327
- * memory leakage inside the type's output routine.
328
- *
329
- * Here we catch undefined bytes in tuples that are returned to the
344
+ * Here we catch undefined bytes in datums that are returned to the
330
345
* client without hitting disk; see comments at the related check in
331
- * PageAddItem(). Whether to test before or after detoast is somewhat
332
- * arbitrary, as is whether to test external/compressed data at all.
333
- * Undefined bytes in the pre-toast datum will have triggered Valgrind
334
- * errors in the compressor or toaster; any error detected here for
335
- * such datums would indicate an (unlikely) bug in a type-independent
336
- * facility. Therefore, this test is most useful for uncompressed,
337
- * non-external datums.
338
- *
339
- * We don't presently bother checking non-varlena datums for undefined
340
- * data. PageAddItem() does check them.
346
+ * PageAddItem(). This test is most useful for uncompressed,
347
+ * non-external datums, but we're quite likely to see such here when
348
+ * testing new C functions.
341
349
*/
342
350
if (thisState -> typisvarlena )
343
- {
344
- VALGRIND_CHECK_MEM_IS_DEFINED (origattr , VARSIZE_ANY (origattr ));
345
-
346
- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
347
- }
348
- else
349
- attr = origattr ;
351
+ VALGRIND_CHECK_MEM_IS_DEFINED (DatumGetPointer (attr ),
352
+ VARSIZE_ANY (attr ));
350
353
351
354
if (thisState -> format == 0 )
352
355
{
@@ -355,7 +358,6 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
355
358
356
359
outputstr = OutputFunctionCall (& thisState -> finfo , attr );
357
360
pq_sendcountedtext (& buf , outputstr , strlen (outputstr ), false);
358
- pfree (outputstr );
359
361
}
360
362
else
361
363
{
@@ -366,15 +368,14 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
366
368
pq_sendint (& buf , VARSIZE (outputbytes ) - VARHDRSZ , 4 );
367
369
pq_sendbytes (& buf , VARDATA (outputbytes ),
368
370
VARSIZE (outputbytes ) - VARHDRSZ );
369
- pfree (outputbytes );
370
371
}
371
-
372
- /* Clean up detoasted copy, if any */
373
- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
374
- pfree (DatumGetPointer (attr ));
375
372
}
376
373
377
374
pq_endmessage (& buf );
375
+
376
+ /* Return to caller's context, and flush row's temporary memory */
377
+ MemoryContextSwitchTo (oldcontext );
378
+ MemoryContextReset (myState -> tmpcontext );
378
379
}
379
380
380
381
/* ----------------
@@ -386,6 +387,7 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
386
387
{
387
388
TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
388
389
DR_printtup * myState = (DR_printtup * ) self ;
390
+ MemoryContext oldcontext ;
389
391
StringInfoData buf ;
390
392
int natts = typeinfo -> natts ;
391
393
int i ,
@@ -399,6 +401,9 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
399
401
/* Make sure the tuple is fully deconstructed */
400
402
slot_getallattrs (slot );
401
403
404
+ /* Switch into per-row context so we can recover memory below */
405
+ oldcontext = MemoryContextSwitchTo (myState -> tmpcontext );
406
+
402
407
/*
403
408
* tell the frontend to expect new tuple data (in ASCII style)
404
409
*/
@@ -430,34 +435,23 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
430
435
for (i = 0 ; i < natts ; ++ i )
431
436
{
432
437
PrinttupAttrInfo * thisState = myState -> myinfo + i ;
433
- Datum origattr = slot -> tts_values [i ],
434
- attr ;
438
+ Datum attr = slot -> tts_values [i ];
435
439
char * outputstr ;
436
440
437
441
if (slot -> tts_isnull [i ])
438
442
continue ;
439
443
440
444
Assert (thisState -> format == 0 );
441
445
442
- /*
443
- * If we have a toasted datum, forcibly detoast it here to avoid
444
- * memory leakage inside the type's output routine.
445
- */
446
- if (thisState -> typisvarlena )
447
- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
448
- else
449
- attr = origattr ;
450
-
451
446
outputstr = OutputFunctionCall (& thisState -> finfo , attr );
452
447
pq_sendcountedtext (& buf , outputstr , strlen (outputstr ), true);
453
- pfree (outputstr );
454
-
455
- /* Clean up detoasted copy, if any */
456
- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
457
- pfree (DatumGetPointer (attr ));
458
448
}
459
449
460
450
pq_endmessage (& buf );
451
+
452
+ /* Return to caller's context, and flush row's temporary memory */
453
+ MemoryContextSwitchTo (oldcontext );
454
+ MemoryContextReset (myState -> tmpcontext );
461
455
}
462
456
463
457
/* ----------------
@@ -474,6 +468,10 @@ printtup_shutdown(DestReceiver *self)
474
468
myState -> myinfo = NULL ;
475
469
476
470
myState -> attrinfo = NULL ;
471
+
472
+ if (myState -> tmpcontext )
473
+ MemoryContextDelete (myState -> tmpcontext );
474
+ myState -> tmpcontext = NULL ;
477
475
}
478
476
479
477
/* ----------------
@@ -536,39 +534,23 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
536
534
TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
537
535
int natts = typeinfo -> natts ;
538
536
int i ;
539
- Datum origattr ,
540
- attr ;
537
+ Datum attr ;
541
538
char * value ;
542
539
bool isnull ;
543
540
Oid typoutput ;
544
541
bool typisvarlena ;
545
542
546
543
for (i = 0 ; i < natts ; ++ i )
547
544
{
548
- origattr = slot_getattr (slot , i + 1 , & isnull );
545
+ attr = slot_getattr (slot , i + 1 , & isnull );
549
546
if (isnull )
550
547
continue ;
551
548
getTypeOutputInfo (typeinfo -> attrs [i ]-> atttypid ,
552
549
& typoutput , & typisvarlena );
553
550
554
- /*
555
- * If we have a toasted datum, forcibly detoast it here to avoid
556
- * memory leakage inside the type's output routine.
557
- */
558
- if (typisvarlena )
559
- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
560
- else
561
- attr = origattr ;
562
-
563
551
value = OidOutputFunctionCall (typoutput , attr );
564
552
565
553
printatt ((unsigned ) i + 1 , typeinfo -> attrs [i ], value );
566
-
567
- pfree (value );
568
-
569
- /* Clean up detoasted copy, if any */
570
- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
571
- pfree (DatumGetPointer (attr ));
572
554
}
573
555
printf ("\t----\n" );
574
556
}
@@ -587,6 +569,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
587
569
{
588
570
TupleDesc typeinfo = slot -> tts_tupleDescriptor ;
589
571
DR_printtup * myState = (DR_printtup * ) self ;
572
+ MemoryContext oldcontext ;
590
573
StringInfoData buf ;
591
574
int natts = typeinfo -> natts ;
592
575
int i ,
@@ -600,6 +583,9 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
600
583
/* Make sure the tuple is fully deconstructed */
601
584
slot_getallattrs (slot );
602
585
586
+ /* Switch into per-row context so we can recover memory below */
587
+ oldcontext = MemoryContextSwitchTo (myState -> tmpcontext );
588
+
603
589
/*
604
590
* tell the frontend to expect new tuple data (in binary style)
605
591
*/
@@ -631,35 +617,23 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
631
617
for (i = 0 ; i < natts ; ++ i )
632
618
{
633
619
PrinttupAttrInfo * thisState = myState -> myinfo + i ;
634
- Datum origattr = slot -> tts_values [i ],
635
- attr ;
620
+ Datum attr = slot -> tts_values [i ];
636
621
bytea * outputbytes ;
637
622
638
623
if (slot -> tts_isnull [i ])
639
624
continue ;
640
625
641
626
Assert (thisState -> format == 1 );
642
627
643
- /*
644
- * If we have a toasted datum, forcibly detoast it here to avoid
645
- * memory leakage inside the type's output routine.
646
- */
647
- if (thisState -> typisvarlena )
648
- attr = PointerGetDatum (PG_DETOAST_DATUM (origattr ));
649
- else
650
- attr = origattr ;
651
-
652
628
outputbytes = SendFunctionCall (& thisState -> finfo , attr );
653
- /* We assume the result will not have been toasted */
654
629
pq_sendint (& buf , VARSIZE (outputbytes ) - VARHDRSZ , 4 );
655
630
pq_sendbytes (& buf , VARDATA (outputbytes ),
656
631
VARSIZE (outputbytes ) - VARHDRSZ );
657
- pfree (outputbytes );
658
-
659
- /* Clean up detoasted copy, if any */
660
- if (DatumGetPointer (attr ) != DatumGetPointer (origattr ))
661
- pfree (DatumGetPointer (attr ));
662
632
}
663
633
664
634
pq_endmessage (& buf );
635
+
636
+ /* Return to caller's context, and flush row's temporary memory */
637
+ MemoryContextSwitchTo (oldcontext );
638
+ MemoryContextReset (myState -> tmpcontext );
665
639
}
0 commit comments