Обсуждение: Is this a memory leak in libpqrcv_processTuples()?

Поиск
Список
Период
Сортировка

Is this a memory leak in libpqrcv_processTuples()?

От
Chao Li
Дата:
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,
whenI 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
itselfas 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
realmemory leak worthing a fix? 

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







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

От
"Dewei Dai"
Дата:
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@163.com
 
From: Chao Li
Date: 2026-01-06 14:03
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/
 
 
 
 
 

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

От
Neil Chen
Дата:
Hi Chao,

As you mentioned, attinmeta will be automatically freed when the memory context is destroyed. If our concern here is that repeated calls to walrcv_exec in some section of the code are causing a large number of attinmeta objects to remain unreleased(I’m not sure if anyone would use walrcv_exec to do something like this), this would seem to imply that we are repeatedly constructing identical attinmeta instances – for example:

while (loop_condition) {
    sprintf(query, "xxx where xx = %d", index++);
    res = walrcv_exec(conn, query, ...);
}

It is rare to encounter a scenario where walrcv_exec is called multiple times and each invocation uses a query with different attrs. Therefore, if this loop scenario is indeed the case, should we consider avoiding the repeated construction of tupdesc/attinmeta?