Commit 4a2516a7 authored by Tom Lane's avatar Tom Lane

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.
parent e6e38b4a
...@@ -40,6 +40,15 @@ Datum xpath_table(PG_FUNCTION_ARGS); ...@@ -40,6 +40,15 @@ Datum xpath_table(PG_FUNCTION_ARGS);
void pgxml_parser_init(void); void pgxml_parser_init(void);
/* workspace for pgxml_xpath() */
typedef struct
{
xmlDocPtr doctree;
xmlXPathContextPtr ctxt;
xmlXPathObjectPtr res;
} xpath_workspace;
/* local declarations */ /* local declarations */
static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset, static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
...@@ -51,7 +60,10 @@ static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag, ...@@ -51,7 +60,10 @@ static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag,
static xmlChar *pgxml_texttoxmlchar(text *textstring); static xmlChar *pgxml_texttoxmlchar(text *textstring);
static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath); static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath,
xpath_workspace *workspace);
static void cleanup_workspace(xpath_workspace *workspace);
/* /*
...@@ -221,25 +233,22 @@ PG_FUNCTION_INFO_V1(xpath_nodeset); ...@@ -221,25 +233,22 @@ PG_FUNCTION_INFO_V1(xpath_nodeset);
Datum Datum
xpath_nodeset(PG_FUNCTION_ARGS) xpath_nodeset(PG_FUNCTION_ARGS)
{ {
xmlChar *xpath, text *document = PG_GETARG_TEXT_P(0);
*toptag, text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
*septag; xmlChar *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
int32 pathsize; xmlChar *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
text *xpathsupp, xmlChar *xpath;
*xpres; text *xpres;
xmlXPathObjectPtr res;
/* PG_GETARG_TEXT_P(0) is document buffer */ xpath_workspace workspace;
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2)); xpath = pgxml_texttoxmlchar(xpathsupp);
septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
pathsize = VARSIZE(xpathsupp) - VARHDRSZ; res = pgxml_xpath(document, xpath, &workspace);
xpath = pgxml_texttoxmlchar(xpathsupp); xpres = pgxml_result_to_text(res, toptag, septag, NULL);
xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), cleanup_workspace(&workspace);
toptag, septag, NULL);
pfree(xpath); pfree(xpath);
...@@ -257,23 +266,21 @@ PG_FUNCTION_INFO_V1(xpath_list); ...@@ -257,23 +266,21 @@ PG_FUNCTION_INFO_V1(xpath_list);
Datum Datum
xpath_list(PG_FUNCTION_ARGS) xpath_list(PG_FUNCTION_ARGS)
{ {
xmlChar *xpath, text *document = PG_GETARG_TEXT_P(0);
*plainsep; text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
int32 pathsize; xmlChar *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
text *xpathsupp, xmlChar *xpath;
*xpres; text *xpres;
xmlXPathObjectPtr res;
/* PG_GETARG_TEXT_P(0) is document buffer */ xpath_workspace workspace;
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2)); xpath = pgxml_texttoxmlchar(xpathsupp);
pathsize = VARSIZE(xpathsupp) - VARHDRSZ; res = pgxml_xpath(document, xpath, &workspace);
xpath = pgxml_texttoxmlchar(xpathsupp); xpres = pgxml_result_to_text(res, NULL, NULL, plainsep);
xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), cleanup_workspace(&workspace);
NULL, NULL, plainsep);
pfree(xpath); pfree(xpath);
...@@ -288,13 +295,13 @@ PG_FUNCTION_INFO_V1(xpath_string); ...@@ -288,13 +295,13 @@ PG_FUNCTION_INFO_V1(xpath_string);
Datum Datum
xpath_string(PG_FUNCTION_ARGS) xpath_string(PG_FUNCTION_ARGS)
{ {
text *document = PG_GETARG_TEXT_P(0);
text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
xmlChar *xpath; xmlChar *xpath;
int32 pathsize; int32 pathsize;
text *xpathsupp, text *xpres;
*xpres; xmlXPathObjectPtr res;
xpath_workspace workspace;
/* PG_GETARG_TEXT_P(0) is document buffer */
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
pathsize = VARSIZE(xpathsupp) - VARHDRSZ; pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
...@@ -305,13 +312,16 @@ xpath_string(PG_FUNCTION_ARGS) ...@@ -305,13 +312,16 @@ xpath_string(PG_FUNCTION_ARGS)
/* We could try casting to string using the libxml function? */ /* We could try casting to string using the libxml function? */
xpath = (xmlChar *) palloc(pathsize + 9); xpath = (xmlChar *) palloc(pathsize + 9);
memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
strncpy((char *) xpath, "string(", 7); strncpy((char *) xpath, "string(", 7);
memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
xpath[pathsize + 7] = ')'; xpath[pathsize + 7] = ')';
xpath[pathsize + 8] = '\0'; xpath[pathsize + 8] = '\0';
xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath), res = pgxml_xpath(document, xpath, &workspace);
NULL, NULL, NULL);
xpres = pgxml_result_to_text(res, NULL, NULL, NULL);
cleanup_workspace(&workspace);
pfree(xpath); pfree(xpath);
...@@ -326,21 +336,17 @@ PG_FUNCTION_INFO_V1(xpath_number); ...@@ -326,21 +336,17 @@ PG_FUNCTION_INFO_V1(xpath_number);
Datum Datum
xpath_number(PG_FUNCTION_ARGS) xpath_number(PG_FUNCTION_ARGS)
{ {
text *document = PG_GETARG_TEXT_P(0);
text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
xmlChar *xpath; xmlChar *xpath;
int32 pathsize;
text *xpathsupp;
float4 fRes; float4 fRes;
xmlXPathObjectPtr res; xmlXPathObjectPtr res;
xpath_workspace workspace;
/* PG_GETARG_TEXT_P(0) is document buffer */
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
xpath = pgxml_texttoxmlchar(xpathsupp); xpath = pgxml_texttoxmlchar(xpathsupp);
res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath); res = pgxml_xpath(document, xpath, &workspace);
pfree(xpath); pfree(xpath);
if (res == NULL) if (res == NULL)
...@@ -348,6 +354,8 @@ xpath_number(PG_FUNCTION_ARGS) ...@@ -348,6 +354,8 @@ xpath_number(PG_FUNCTION_ARGS)
fRes = xmlXPathCastToNumber(res); fRes = xmlXPathCastToNumber(res);
cleanup_workspace(&workspace);
if (xmlXPathIsNaN(fRes)) if (xmlXPathIsNaN(fRes))
PG_RETURN_NULL(); PG_RETURN_NULL();
...@@ -360,21 +368,17 @@ PG_FUNCTION_INFO_V1(xpath_bool); ...@@ -360,21 +368,17 @@ PG_FUNCTION_INFO_V1(xpath_bool);
Datum Datum
xpath_bool(PG_FUNCTION_ARGS) xpath_bool(PG_FUNCTION_ARGS)
{ {
text *document = PG_GETARG_TEXT_P(0);
text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
xmlChar *xpath; xmlChar *xpath;
int32 pathsize;
text *xpathsupp;
int bRes; int bRes;
xmlXPathObjectPtr res; xmlXPathObjectPtr res;
xpath_workspace workspace;
/* PG_GETARG_TEXT_P(0) is document buffer */
xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */
pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
xpath = pgxml_texttoxmlchar(xpathsupp); xpath = pgxml_texttoxmlchar(xpathsupp);
res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath); res = pgxml_xpath(document, xpath, &workspace);
pfree(xpath); pfree(xpath);
if (res == NULL) if (res == NULL)
...@@ -382,6 +386,8 @@ xpath_bool(PG_FUNCTION_ARGS) ...@@ -382,6 +386,8 @@ xpath_bool(PG_FUNCTION_ARGS)
bRes = xmlXPathCastToBoolean(res); bRes = xmlXPathCastToBoolean(res);
cleanup_workspace(&workspace);
PG_RETURN_BOOL(bRes); PG_RETURN_BOOL(bRes);
} }
...@@ -390,49 +396,61 @@ xpath_bool(PG_FUNCTION_ARGS) ...@@ -390,49 +396,61 @@ xpath_bool(PG_FUNCTION_ARGS)
/* Core function to evaluate XPath query */ /* Core function to evaluate XPath query */
static xmlXPathObjectPtr static xmlXPathObjectPtr
pgxml_xpath(text *document, xmlChar *xpath) pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace)
{ {
xmlDocPtr doctree; int32 docsize = VARSIZE(document) - VARHDRSZ;
xmlXPathContextPtr ctxt;
xmlXPathObjectPtr res; xmlXPathObjectPtr res;
xmlXPathCompExprPtr comppath; xmlXPathCompExprPtr comppath;
int32 docsize;
docsize = VARSIZE(document) - VARHDRSZ; workspace->doctree = NULL;
workspace->ctxt = NULL;
workspace->res = NULL;
pgxml_parser_init(); pgxml_parser_init();
doctree = xmlParseMemory((char *) VARDATA(document), docsize); workspace->doctree = xmlParseMemory((char *) VARDATA(document), docsize);
if (doctree == NULL) if (workspace->doctree == NULL)
return NULL; /* not well-formed */ return NULL; /* not well-formed */
ctxt = xmlXPathNewContext(doctree); workspace->ctxt = xmlXPathNewContext(workspace->doctree);
ctxt->node = xmlDocGetRootElement(doctree); workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);
/* compile the path */ /* compile the path */
comppath = xmlXPathCompile(xpath); comppath = xmlXPathCompile(xpath);
if (comppath == NULL) if (comppath == NULL)
{ {
xmlFreeDoc(doctree); cleanup_workspace(workspace);
xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
"XPath Syntax Error"); "XPath Syntax Error");
} }
/* Now evaluate the path expression. */ /* Now evaluate the path expression. */
res = xmlXPathCompiledEval(comppath, ctxt); res = xmlXPathCompiledEval(comppath, workspace->ctxt);
workspace->res = res;
xmlXPathFreeCompExpr(comppath); xmlXPathFreeCompExpr(comppath);
if (res == NULL) if (res == NULL)
{ cleanup_workspace(workspace);
xmlXPathFreeContext(ctxt);
xmlFreeDoc(doctree);
return NULL;
}
/* xmlFreeDoc(doctree); */
return res; return res;
} }
/* Clean up after processing the result of pgxml_xpath() */
static void
cleanup_workspace(xpath_workspace *workspace)
{
if (workspace->res)
xmlXPathFreeObject(workspace->res);
workspace->res = NULL;
if (workspace->ctxt)
xmlXPathFreeContext(workspace->ctxt);
workspace->ctxt = NULL;
if (workspace->doctree)
xmlFreeDoc(workspace->doctree);
workspace->doctree = NULL;
}
static text * static text *
pgxml_result_to_text(xmlXPathObjectPtr res, pgxml_result_to_text(xmlXPathObjectPtr res,
xmlChar *toptag, xmlChar *toptag,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment