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, [email protected] 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/
