Skip to content

Commit 35a3def

Browse files
committed
Fix significant memory leak in contrib/xml2 functions.
Most of the functions that execute XPath queries leaked the data structures created by libxml2. This memory would not be recovered until end of session, so it mounts up pretty quickly in any serious use of the feature. Per report from Pavel Stehule, though this isn't his patch. Back-patch to all supported branches.
1 parent 268da29 commit 35a3def

File tree

1 file changed

+90
-72
lines changed

1 file changed

+90
-72
lines changed

contrib/xml2/xpath.c

+90-72
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ Datum xpath_table(PG_FUNCTION_ARGS);
3232
void elog_error(const char *explain, bool force);
3333
void pgxml_parser_init(void);
3434

35+
/* workspace for pgxml_xpath() */
36+
37+
typedef struct
38+
{
39+
xmlDocPtr doctree;
40+
xmlXPathContextPtr ctxt;
41+
xmlXPathObjectPtr res;
42+
} xpath_workspace;
43+
3544
/* local declarations */
3645

3746
static void pgxml_errorHandler(void *ctxt, const char *msg,...);
@@ -45,7 +54,10 @@ static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag,
4554

4655
static xmlChar *pgxml_texttoxmlchar(text *textstring);
4756

48-
static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath);
57+
static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath,
58+
xpath_workspace *workspace);
59+
60+
static void cleanup_workspace(xpath_workspace *workspace);
4961

5062
/* Global variables */
5163
static char *pgxml_errorMsg = NULL; /* overall error message */
@@ -289,25 +301,22 @@ PG_FUNCTION_INFO_V1(xpath_nodeset);
289301
Datum
290302
xpath_nodeset(PG_FUNCTION_ARGS)
291303
{
292-
xmlChar *xpath,
293-
*toptag,
294-
*septag;
295-
int32 pathsize;
296-
text *xpathsupp,
297-
*xpres;
298-
299-
/* PG_GETARG_TEXT_P(0) is document buffer */
300-
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
304+
text *document = PG_GETARG_TEXT_P(0);
305+
text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
306+
xmlChar *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
307+
xmlChar *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
308+
xmlChar *xpath;
309+
text *xpres;
310+
xmlXPathObjectPtr res;
311+
xpath_workspace workspace;
301312

302-
toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
303-
septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
313+
xpath = pgxml_texttoxmlchar(xpathsupp);
304314

305-
pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
315+
res = pgxml_xpath(document, xpath, &workspace);
306316

307-
xpath = pgxml_texttoxmlchar(xpathsupp);
317+
xpres = pgxml_result_to_text(res, toptag, septag, NULL);
308318

309-
xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
310-
toptag, septag, NULL);
319+
cleanup_workspace(&workspace);
311320

312321
pfree(xpath);
313322

@@ -325,23 +334,21 @@ PG_FUNCTION_INFO_V1(xpath_list);
325334
Datum
326335
xpath_list(PG_FUNCTION_ARGS)
327336
{
328-
xmlChar *xpath,
329-
*plainsep;
330-
int32 pathsize;
331-
text *xpathsupp,
332-
*xpres;
333-
334-
/* PG_GETARG_TEXT_P(0) is document buffer */
335-
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
337+
text *document = PG_GETARG_TEXT_P(0);
338+
text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
339+
xmlChar *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
340+
xmlChar *xpath;
341+
text *xpres;
342+
xmlXPathObjectPtr res;
343+
xpath_workspace workspace;
336344

337-
plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
345+
xpath = pgxml_texttoxmlchar(xpathsupp);
338346

339-
pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
347+
res = pgxml_xpath(document, xpath, &workspace);
340348

341-
xpath = pgxml_texttoxmlchar(xpathsupp);
349+
xpres = pgxml_result_to_text(res, NULL, NULL, plainsep);
342350

343-
xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
344-
NULL, NULL, plainsep);
351+
cleanup_workspace(&workspace);
345352

346353
pfree(xpath);
347354

@@ -356,13 +363,13 @@ PG_FUNCTION_INFO_V1(xpath_string);
356363
Datum
357364
xpath_string(PG_FUNCTION_ARGS)
358365
{
366+
text *document = PG_GETARG_TEXT_P(0);
367+
text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
359368
xmlChar *xpath;
360369
int32 pathsize;
361-
text *xpathsupp,
362-
*xpres;
363-
364-
/* PG_GETARG_TEXT_P(0) is document buffer */
365-
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
370+
text *xpres;
371+
xmlXPathObjectPtr res;
372+
xpath_workspace workspace;
366373

367374
pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
368375

@@ -373,13 +380,16 @@ xpath_string(PG_FUNCTION_ARGS)
373380
/* We could try casting to string using the libxml function? */
374381

375382
xpath = (xmlChar *) palloc(pathsize + 9);
376-
memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
377383
strncpy((char *) xpath, "string(", 7);
384+
memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
378385
xpath[pathsize + 7] = ')';
379386
xpath[pathsize + 8] = '\0';
380387

381-
xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
382-
NULL, NULL, NULL);
388+
res = pgxml_xpath(document, xpath, &workspace);
389+
390+
xpres = pgxml_result_to_text(res, NULL, NULL, NULL);
391+
392+
cleanup_workspace(&workspace);
383393

384394
pfree(xpath);
385395

@@ -394,28 +404,26 @@ PG_FUNCTION_INFO_V1(xpath_number);
394404
Datum
395405
xpath_number(PG_FUNCTION_ARGS)
396406
{
407+
text *document = PG_GETARG_TEXT_P(0);
408+
text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
397409
xmlChar *xpath;
398-
int32 pathsize;
399-
text *xpathsupp;
400410
float4 fRes;
401-
402411
xmlXPathObjectPtr res;
403-
404-
/* PG_GETARG_TEXT_P(0) is document buffer */
405-
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
406-
407-
pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
412+
xpath_workspace workspace;
408413

409414
xpath = pgxml_texttoxmlchar(xpathsupp);
410415

411-
res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);
416+
res = pgxml_xpath(document, xpath, &workspace);
417+
412418
pfree(xpath);
413419

414420
if (res == NULL)
415421
PG_RETURN_NULL();
416422

417423
fRes = xmlXPathCastToNumber(res);
418424

425+
cleanup_workspace(&workspace);
426+
419427
if (xmlXPathIsNaN(fRes))
420428
PG_RETURN_NULL();
421429

@@ -428,28 +436,26 @@ PG_FUNCTION_INFO_V1(xpath_bool);
428436
Datum
429437
xpath_bool(PG_FUNCTION_ARGS)
430438
{
439+
text *document = PG_GETARG_TEXT_P(0);
440+
text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
431441
xmlChar *xpath;
432-
int32 pathsize;
433-
text *xpathsupp;
434442
int bRes;
435-
436443
xmlXPathObjectPtr res;
437-
438-
/* PG_GETARG_TEXT_P(0) is document buffer */
439-
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
440-
441-
pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
444+
xpath_workspace workspace;
442445

443446
xpath = pgxml_texttoxmlchar(xpathsupp);
444447

445-
res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);
448+
res = pgxml_xpath(document, xpath, &workspace);
449+
446450
pfree(xpath);
447451

448452
if (res == NULL)
449453
PG_RETURN_BOOL(false);
450454

451455
bRes = xmlXPathCastToBoolean(res);
452456

457+
cleanup_workspace(&workspace);
458+
453459
PG_RETURN_BOOL(bRes);
454460
}
455461

@@ -458,48 +464,60 @@ xpath_bool(PG_FUNCTION_ARGS)
458464
/* Core function to evaluate XPath query */
459465

460466
static xmlXPathObjectPtr
461-
pgxml_xpath(text *document, xmlChar *xpath)
467+
pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace)
462468
{
463-
xmlDocPtr doctree;
464-
xmlXPathContextPtr ctxt;
469+
int32 docsize = VARSIZE(document) - VARHDRSZ;
465470
xmlXPathObjectPtr res;
466471
xmlXPathCompExprPtr comppath;
467-
int32 docsize;
468472

469-
docsize = VARSIZE(document) - VARHDRSZ;
473+
workspace->doctree = NULL;
474+
workspace->ctxt = NULL;
475+
workspace->res = NULL;
470476

471477
pgxml_parser_init();
472478

473-
doctree = xmlParseMemory((char *) VARDATA(document), docsize);
474-
if (doctree == NULL)
479+
workspace->doctree = xmlParseMemory((char *) VARDATA(document), docsize);
480+
if (workspace->doctree == NULL)
475481
return NULL; /* not well-formed */
476482

477-
ctxt = xmlXPathNewContext(doctree);
478-
ctxt->node = xmlDocGetRootElement(doctree);
483+
workspace->ctxt = xmlXPathNewContext(workspace->doctree);
484+
workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);
479485

480486
/* compile the path */
481487
comppath = xmlXPathCompile(xpath);
482488
if (comppath == NULL)
483489
{
484-
xmlFreeDoc(doctree);
490+
cleanup_workspace(workspace);
485491
elog_error("XPath Syntax Error", true);
486492
}
487493

488494
/* Now evaluate the path expression. */
489-
res = xmlXPathCompiledEval(comppath, ctxt);
495+
res = xmlXPathCompiledEval(comppath, workspace->ctxt);
496+
workspace->res = res;
497+
490498
xmlXPathFreeCompExpr(comppath);
491499

492500
if (res == NULL)
493-
{
494-
xmlXPathFreeContext(ctxt);
495-
xmlFreeDoc(doctree);
501+
cleanup_workspace(workspace);
496502

497-
return NULL;
498-
}
499-
/* xmlFreeDoc(doctree); */
500503
return res;
501504
}
502505

506+
/* Clean up after processing the result of pgxml_xpath() */
507+
static void
508+
cleanup_workspace(xpath_workspace *workspace)
509+
{
510+
if (workspace->res)
511+
xmlXPathFreeObject(workspace->res);
512+
workspace->res = NULL;
513+
if (workspace->ctxt)
514+
xmlXPathFreeContext(workspace->ctxt);
515+
workspace->ctxt = NULL;
516+
if (workspace->doctree)
517+
xmlFreeDoc(workspace->doctree);
518+
workspace->doctree = NULL;
519+
}
520+
503521
static text *
504522
pgxml_result_to_text(xmlXPathObjectPtr res,
505523
xmlChar *toptag,

0 commit comments

Comments
 (0)