8000 Fix memory leaks in record_out() and record_send(). · danielcode/postgres@5355e39 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5355e39

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 3154177 commit 5355e39

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
@@ -31,6 +31,7 @@ typedef struct ColumnIOData
3131
Oid column_type;
3232
Oid typiofunc;
3333
Oid typioparam;
34+
bool typisvarlena;
3435
FmgrInfo proc;
3536
} ColumnIOData;
3637

@@ -363,6 +364,7 @@ record_out(PG_FUNCTION_ARGS)
363364
{
364365
ColumnIOData *column_info = &my_extra->columns[i];
365366
Oid column_type = tupdesc->attrs[i]->atttypid;
367+
Datum attr;
366368
char *value;
367369
char *tmp;
368370
bool nq;
@@ -386,17 +388,24 @@ record_out(PG_FUNCTION_ARGS)
386388
*/
387389
if (column_info->column_type != column_type)
388390
{
389-
bool typIsVarlena;
390-
391391
getTypeOutputInfo(column_type,
392392
&column_info->typiofunc,
393-
&typIsVarlena);
393+
&column_info->typisvarlena);
394394
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
395395
fcinfo->flinfo->fn_mcxt);
396396
column_info->column_type = column_type;
397397
}
398398

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

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

416425
/* And emit the string */
417426
if (nq)
418-
appendStringInfoChar(&buf, '"');
427+
appendStringInfoCharMacro(&buf, '"');
419428
for (tmp = value; *tmp; tmp++)
420429
{
421430
char ch = *tmp;
422431

423432
if (ch == '"' || ch == '\\')
424-
appendStringInfoChar(&buf, ch);
425-
appendStringInfoChar(&buf, ch);
433+
appendStringInfoCharMacro(&buf, ch);
434+
appendStringInfoCharMacro(&buf, ch);
426435
}
427436
if (nq)
428-
appendStringInfoChar(&buf, '"');
437+
appendStringInfoCharMacro(&buf, '"');
438+
439+
pfree(value);
440+
441+
/* Clean up detoasted copy, if any */
442+
if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
443+
pfree(DatumGetPointer(attr));
429444
}
430445

431446
appendStringInfoChar(&buf, ')');
@@ -713,6 +728,7 @@ record_send(PG_FUNCTION_ARGS)
713728
{
714729
ColumnIOData *column_info = &my_extra->columns[i];
715730
Oid column_type = tupdesc->attrs[i]->atttypid;
731+
Datum attr;
716732
bytea *outputbytes;
717733

718734
/* Ignore dropped columns in datatype */
@@ -733,23 +749,35 @@ record_send(PG_FUNCTION_ARGS)
733749
*/
734750
if (column_info->column_type != column_type)
735751
{
736-
bool typIsVarlena;
737-
738752
getTypeBinaryOutputInfo(column_type,
739753
&column_info->typiofunc,
740-
&typIsVarlena);
754+
&column_info->typisvarlena);
741755
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
742756
fcinfo->flinfo->fn_mcxt);
743757
column_info->column_type = column_type;
744758
}
745759

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

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

755783
pfree(values);

0 commit comments

Comments
 (0)
0