Skip to content

Commit 273986b

Browse files
committed
Fix memory leaks in record_out() and record_send().
record_out() leaks memory: it fails to free the strings returned by the per-column output functions, and also is careless about detoasted values. This results in a query-lifespan memory leakage when returning composite values to the client, because printtup() runs the output functions in the query-lifespan memory context. Fix it to handle these issues the same way printtup() does. Also fix a similar leakage in record_send(). (At some point we might want to try to run output functions in shorter-lived memory contexts, so that we don't need a zero-leakage policy for them. But that would be a significantly more invasive patch, which doesn't seem like material for back-patching.) In passing, use appendStringInfoCharMacro instead of appendStringInfoChar in the innermost data-copying loop of record_out, to try to shave a few cycles from this function's runtime. Per trouble report from Carlos Henrique Reimer. Back-patch to all supported versions.
1 parent d9fad10 commit 273986b

File tree

1 file changed

+40
-12
lines changed

1 file changed

+40
-12
lines changed

src/backend/utils/adt/rowtypes.c

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ typedef struct ColumnIOData
3232
Oid column_type;
3333
Oid typiofunc;
3434
Oid typioparam;
35+
bool typisvarlena;
3536
FmgrInfo proc;
3637
} ColumnIOData;
3738

@@ -364,6 +365,7 @@ record_out(PG_FUNCTION_ARGS)
364365
{
365366
ColumnIOData *column_info = &my_extra->columns[i];
366367
Oid column_type = tupdesc->attrs[i]->atttypid;
368+
Datum attr;
367369
char *value;
368370
char *tmp;
369371
bool nq;
@@ -387,17 +389,24 @@ record_out(PG_FUNCTION_ARGS)
387389
*/
388390
if (column_info->column_type != column_type)
389391
{
390-
bool typIsVarlena;
391-
392392
getTypeOutputInfo(column_type,
393393
&column_info->typiofunc,
394-
&typIsVarlena);
394+
&column_info->typisvarlena);
395395
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
396396
fcinfo->flinfo->fn_mcxt);
397397
column_info->column_type = column_type;
398398
}
399399

400-
value = OutputFunctionCall(&column_info->proc, values[i]);
400+
/*
401+
* If we have a toasted datum, forcibly detoast it here to avoid
402+
* memory leakage inside the type's output routine.
403+
*/
404+
if (column_info->typisvarlena)
405+
attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
406+
else
407+
attr = values[i];
408+
409+
value = OutputFunctionCall(&column_info->proc, attr);
401410

402411
/* Detect whether we need double quotes for this value */
403412
nq = (value[0] == '\0'); /* force quotes for empty string */
@@ -416,17 +425,23 @@ record_out(PG_FUNCTION_ARGS)
416425

417426
/* And emit the string */
418427
if (nq)
419-
appendStringInfoChar(&buf, '"');
428+
appendStringInfoCharMacro(&buf, '"');
420429
for (tmp = value; *tmp; tmp++)
421430
{
422431
char ch = *tmp;
423432

424433
if (ch == '"' || ch == '\\')
425-
appendStringInfoChar(&buf, ch);
426-
appendStringInfoChar(&buf, ch);
434+
appendStringInfoCharMacro(&buf, ch);
435+
appendStringInfoCharMacro(&buf, ch);
427436
}
428437
if (nq)
429-
appendStringInfoChar(&buf, '"');
438+
appendStringInfoCharMacro(&buf, '"');
439+
440+
pfree(value);
441+
442+
/* Clean up detoasted copy, if any */
443+
if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
444+
pfree(DatumGetPointer(attr));
430445
}
431446

432447
appendStringInfoChar(&buf, ')');
@@ -714,6 +729,7 @@ record_send(PG_FUNCTION_ARGS)
714729
{
715730
ColumnIOData *column_info = &my_extra->columns[i];
716731
Oid column_type = tupdesc->attrs[i]->atttypid;
732+
Datum attr;
717733
bytea *outputbytes;
718734

719735
/* Ignore dropped columns in datatype */
@@ -734,23 +750,35 @@ record_send(PG_FUNCTION_ARGS)
734750
*/
735751
if (column_info->column_type != column_type)
736752
{
737-
bool typIsVarlena;
738-
739753
getTypeBinaryOutputInfo(column_type,
740754
&column_info->typiofunc,
741-
&typIsVarlena);
755+
&column_info->typisvarlena);
742756
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
743757
fcinfo->flinfo->fn_mcxt);
744758
column_info->column_type = column_type;
745759
}
746760

747-
outputbytes = SendFunctionCall(&column_info->proc, values[i]);
761+
/*
762+
* If we have a toasted datum, forcibly detoast it here to avoid
763+
* memory leakage inside the type's output routine.
764+
*/
765+
if (column_info->typisvarlena)
766+
attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
767+
else
768+
attr = values[i];
769+
770+
outputbytes = SendFunctionCall(&column_info->proc, attr);
748771

749772
/* We assume the result will not have been toasted */
750773
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
751774
pq_sendbytes(&buf, VARDATA(outputbytes),
752775
VARSIZE(outputbytes) - VARHDRSZ);
776+
753777
pfree(outputbytes);
778+
779+
/* Clean up detoasted copy, if any */
780+
if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
781+
pfree(DatumGetPointer(attr));
754782
}
755783

756784
pfree(values);

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