Skip to content

Commit c027d84

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 2faab1a commit c027d84

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

@@ -340,6 +341,7 @@ record_out(PG_FUNCTION_ARGS)
340341
{
341342
ColumnIOData *column_info = &my_extra->columns[i];
342343
Oid column_type = tupdesc->attrs[i]->atttypid;
344+
Datum attr;
343345
char *value;
344346
char *tmp;
345347
bool nq;
@@ -363,17 +365,24 @@ record_out(PG_FUNCTION_ARGS)
363365
*/
364366
if (column_info->column_type != column_type)
365367
{
366-
bool typIsVarlena;
367-
368368
getTypeOutputInfo(column_type,
369369
&column_info->typiofunc,
370-
&typIsVarlena);
370+
&column_info->typisvarlena);
371371
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
372372
fcinfo->flinfo->fn_mcxt);
373373
column_info->column_type = column_type;
374374
}
375375

376-
value = OutputFunctionCall(&column_info->proc, values[i]);
376+
/*
377+
* If we have a toasted datum, forcibly detoast it here to avoid
378+
* memory leakage inside the type's output routine.
379+
*/
380+
if (column_info->typisvarlena)
381+
attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
382+
else
383+
attr = values[i];
384+
385+
value = OutputFunctionCall(&column_info->proc, attr);
377386

378387
/* Detect whether we need double quotes for this value */
379388
nq = (value[0] == '\0'); /* force quotes for empty string */
@@ -392,17 +401,23 @@ record_out(PG_FUNCTION_ARGS)
392401

393402
/* And emit the string */
394403
if (nq)
395-
appendStringInfoChar(&buf, '"');
404+
appendStringInfoCharMacro(&buf, '"');
396405
for (tmp = value; *tmp; tmp++)
397406
{
398407
char ch = *tmp;
399408

400409
if (ch == '"' || ch == '\\')
401-
appendStringInfoChar(&buf, ch);
402-
appendStringInfoChar(&buf, ch);
410+
appendStringInfoCharMacro(&buf, ch);
411+
appendStringInfoCharMacro(&buf, ch);
403412
}
404413
if (nq)
405-
appendStringInfoChar(&buf, '"');
414+
appendStringInfoCharMacro(&buf, '"');
415+
416+
pfree(value);
417+
418+
/* Clean up detoasted copy, if any */
419+
if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
420+
pfree(DatumGetPointer(attr));
406421
}
407422

408423
appendStringInfoChar(&buf, ')');
@@ -690,6 +705,7 @@ record_send(PG_FUNCTION_ARGS)
690705
{
691706
ColumnIOData *column_info = &my_extra->columns[i];
692707
Oid column_type = tupdesc->attrs[i]->atttypid;
708+
Datum attr;
693709
bytea *outputbytes;
694710

695711
/* Ignore dropped columns in datatype */
@@ -710,23 +726,35 @@ record_send(PG_FUNCTION_ARGS)
710726
*/
711727
if (column_info->column_type != column_type)
712728
{
713-
bool typIsVarlena;
714-
715729
getTypeBinaryOutputInfo(column_type,
716730
&column_info->typiofunc,
717-
&typIsVarlena);
731+
&column_info->typisvarlena);
718732
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
719733
fcinfo->flinfo->fn_mcxt);
720734
column_info->column_type = column_type;
721735
}
722736

723-
outputbytes = SendFunctionCall(&column_info->proc, values[i]);
737+
/*
738+
* If we have a toasted datum, forcibly detoast it here to avoid
739+
* memory leakage inside the type's output routine.
740+
*/
741+
if (column_info->typisvarlena)
742+
attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
743+
else
744+
attr = values[i];
745+
746+
outputbytes = SendFunctionCall(&column_info->proc, attr);
724747

725748
/* We assume the result will not have been toasted */
726749
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
727750
pq_sendbytes(&buf, VARDATA(outputbytes),
728751
VARSIZE(outputbytes) - VARHDRSZ);
752+
729753
pfree(outputbytes);
754+
755+
/* Clean up detoasted copy, if any */
756+
if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
757+
pfree(DatumGetPointer(attr));
730758
}
731759

732760
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