Re: Is this a memory leak in libpqrcv_processTuples()?

From: "Dewei Dai" <daidewei1970(at)163(dot)com>
To: li(dot)evan(dot)chao <li(dot)evan(dot)chao(at)gmail(dot)com>, "Pgsql Hackers" <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Is this a memory leak in libpqrcv_processTuples()?
Date: 2026-01-07 06:09:57
Message-ID: 202601071031145306275@163.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi chao,

I believe this is a memory leak.
Firstly, the memory context of `attinmeta` is the same as that of `tupledesc`.
Since `tupledesc` needs to be freed in `walrcv_clear_result`,
`attinmeta` also needs to be freed.

I think it can be freed before the `libpqrcv_processTuples`
function returns,and the memory inside `attinmeta` should be freed as well.

Best regards,

daidewei1970(at)163(dot)com

From: Chao Li
Date: 2026-01-06 14:03
To: Postgres hackers
Subject: Is this a memory leak in libpqrcv_processTuples()?
Hi Hackers,

I noticed a memory leak:
```
static void
libpqrcv_processTuples(PGresult *pgres, WalRcvExecResult *walres,
const int nRetTypes, const Oid *retTypes)
{
walres->tuplestore = tuplestore_begin_heap(true, false, work_mem);

/* Create tuple descriptor corresponding to expected result. */
walres->tupledesc = CreateTemplateTupleDesc(nRetTypes);
for (coln = 0; coln < nRetTypes; coln++)
TupleDescInitEntry(walres->tupledesc, (AttrNumber) coln + 1,
PQfname(pgres, coln), retTypes[coln], -1, 0);

attinmeta = TupleDescGetAttInMetadata(walres->tupledesc); <== attinmeta is not free-ed

/* No point in doing more here if there were no tuples returned. */
if (PQntuples(pgres) == 0)
return;
```

I understand that it is a common pattern where memory are free-ed automatically by destroying memory context. However, when I analyzed the usage of libpqrcv_processTuples(), I feel it is a memory leak.

A typical call model is like:
```
Caller of walrcv_exec() {
WalRcvExecResult *res; # define a local variable to store result

res = walrcv_exec(…); # walrcv_exec will return a result

-> walrcv_exec(…) {
WalRcvExecResult *walres = palloc0_object(WalRcvExecResult); # allocate memory for result

walres->status = WALRCV_OK_TUPLES;
libpqrcv_processTuples(pgres, walres, nRetTypes, retTypes); # libpqrcv_processTuples will fill in result

-> libpqrcv_processTuples(… walres …) {

walres->tuplestore = tuplestore_begin_heap() # allocate memory for tuplestore
walres->tupledesc = CreateTemplateTupleDesc(nRetTypes); # allocate memory for tupledesc

attinmeta = TupleDescGetAttInMetadata(walres->tupledesc); # allocate memory for attinmeta
}

return walres;
}

walrcv_clear_result(res); # free res object, nested tuplestore and tupledesc are free-ed as well, but attinmeta is leaked
}
```

We can see that, the result object is explicitly free-ed by walrcv_clear_result. But the memory pointed by attinmeta is leaked, it will only be free-ed when the corresponding memory context is destroyed.

As walrcv_exec() is widely called in a lot of places, it’s hard to tell in which memory context libpqrcv_processTuples() runs, so I think attinmeta should be free-ed.

But I then noticed that, attinmeta is created by TupleDescGetAttInMetadata() that allocates an AttInMetadata object itself as well as several nested objects, and there is not a function to deeply free attinmeta, thus a simple pfree(attinmeta) won’t resolve the leak problem. Does that mean we intentionally to not want to free memory of AttInMetadata?

So, rather than posting a patch directly, I’d like to confirm with folks who are familiar with this area. Is this a real memory leak worthing a fix?

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/





In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message albert tan 2026-01-07 06:12:50 Correct comment wording in extension.c
Previous Message Corey Huinker 2026-01-07 06:04:34 Re: Import Statistics in postgres_fdw before resorting to sampling.