Incremental Update: Thread Progresses Toward Merged Resolution
Summary
Michael Paquier (committer) acknowledged the original patches, identified two additional leak sites, and produced a v2 patch series. Andrey Chernyshov responded with a further incremental fix (v2-0004) addressing a cleanup gap in pgxml_xpath() itself. The thread converged on a merged v2-0001..v2-0004 as the final solution, with an upstream libxml2 API limitation noted but deemed out-of-scope for PostgreSQL.
Key Technical Developments
Two additional error-path leaks identified by Michael (v2 series):
pgxmlNodeSetToText()— missingxmlFree()for theresultvariable returned byxmlXPathCastNodeToString()xmlXPathCtxtCompile()inpgxml_xpath()— ifpg_xml_error_occurred()fires after a successful allocation, the compiled expression leaks
New cleanup gap in pgxml_xpath() identified by Andrey (v2-0004):
The problem: pgxml_xpath() builds an xpath_workspace struct before returning it to callers. Callers wrap calls to pgxml_xpath() in PG_TRY/PG_CATCH, but if an ERROR is thrown inside pgxml_xpath() before it returns, the caller's workspace pointer was never assigned — so the caller's PG_CATCH cannot invoke cleanup_workspace(). The fix adds local cleanup within pgxml_xpath() itself, tracking comppath for the error path and null-checking xmlXPathNewContext() before dereferencing.
The repro script demonstrates ~83 MB/iteration growth on error paths without the fix (XPath syntax errors in a loop), plateauing completely with the fix applied.
Upstream libxml2 API limitation discussed:
Michael identified that xmlXPathCompiledEval() returns NULL both for a valid empty result AND for internal OOM (the chain goes through xmlXPathCompParserContext() which may fail on xmlMalloc()). PostgreSQL cannot distinguish these cases. Both participants agreed this is an upstream issue requiring a new API with explicit error codes, and that PostgreSQL's xml2 code is doing the best it can given the current interface.
Double-cleanup safety:
Michael noted that with v2-0004, cleanup_workspace() could theoretically be called twice (once in pgxml_xpath's local PG_CATCH, once in the caller's PG_CATCH). He confirmed this is safe — the function handles being called on already-cleaned-up state.
Resolution Status
Michael indicated he will merge v2-0001 through v2-0004 into a final commit after a final review pass of all callers. The back-patching question remains: the original commit 732061150b0 was HEAD-only because error-path leaks were considered not worth back-branch churn, but the success-path leaks from the original report are more operationally significant.