]> granicus.if.org Git - postgresql/commitdiff
Fix significant memory leak in contrib/xml2 functions.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 26 Nov 2010 20:21:00 +0000 (15:21 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 26 Nov 2010 20:21:00 +0000 (15:21 -0500)
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.

contrib/xml2/xpath.c

index ce4682942df5d03c1ebe3c1a243b2ee90d08dd1d..44488eac84bcb81bd82bc8c57e82b8a0895263e2 100644 (file)
@@ -40,6 +40,15 @@ Datum                xpath_table(PG_FUNCTION_ARGS);
 
 void           pgxml_parser_init(void);
 
+/* workspace for pgxml_xpath() */
+
+typedef struct
+{
+       xmlDocPtr       doctree;
+       xmlXPathContextPtr ctxt;
+       xmlXPathObjectPtr res;
+} xpath_workspace;
+
 /* local declarations */
 
 static xmlChar *pgxmlNodeSetToText(xmlNodeSetPtr nodeset,
@@ -51,7 +60,10 @@ static text *pgxml_result_to_text(xmlXPathObjectPtr res, xmlChar *toptag,
 
 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);
 
 
 /*
@@ -214,25 +226,22 @@ PG_FUNCTION_INFO_V1(xpath_nodeset);
 Datum
 xpath_nodeset(PG_FUNCTION_ARGS)
 {
-       xmlChar    *xpath,
-                          *toptag,
-                          *septag;
-       int32           pathsize;
-       text       *xpathsupp,
-                          *xpres;
-
-       /* PG_GETARG_TEXT_P(0) is document buffer */
-       xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
+       text       *document = PG_GETARG_TEXT_P(0);
+       text       *xpathsupp = PG_GETARG_TEXT_P(1);    /* XPath expression */
+       xmlChar    *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
+       xmlChar    *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
+       xmlChar    *xpath;
+       text       *xpres;
+       xmlXPathObjectPtr res;
+       xpath_workspace workspace;
 
-       toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
-       septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3));
+       xpath = pgxml_texttoxmlchar(xpathsupp);
 
-       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),
-                                                                toptag, septag, NULL);
+       cleanup_workspace(&workspace);
 
        pfree(xpath);
 
@@ -250,23 +259,21 @@ PG_FUNCTION_INFO_V1(xpath_list);
 Datum
 xpath_list(PG_FUNCTION_ARGS)
 {
-       xmlChar    *xpath,
-                          *plainsep;
-       int32           pathsize;
-       text       *xpathsupp,
-                          *xpres;
-
-       /* PG_GETARG_TEXT_P(0) is document buffer */
-       xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
+       text       *document = PG_GETARG_TEXT_P(0);
+       text       *xpathsupp = PG_GETARG_TEXT_P(1);    /* XPath expression */
+       xmlChar    *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2));
+       xmlChar    *xpath;
+       text       *xpres;
+       xmlXPathObjectPtr res;
+       xpath_workspace workspace;
 
-       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),
-                                                                NULL, NULL, plainsep);
+       cleanup_workspace(&workspace);
 
        pfree(xpath);
 
@@ -281,13 +288,13 @@ PG_FUNCTION_INFO_V1(xpath_string);
 Datum
 xpath_string(PG_FUNCTION_ARGS)
 {
+       text       *document = PG_GETARG_TEXT_P(0);
+       text       *xpathsupp = PG_GETARG_TEXT_P(1);    /* XPath expression */
        xmlChar    *xpath;
        int32           pathsize;
-       text       *xpathsupp,
-                          *xpres;
-
-       /* PG_GETARG_TEXT_P(0) is document buffer */
-       xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
+       text       *xpres;
+       xmlXPathObjectPtr res;
+       xpath_workspace workspace;
 
        pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
 
@@ -298,13 +305,16 @@ xpath_string(PG_FUNCTION_ARGS)
        /* We could try casting to string using the libxml function? */
 
        xpath = (xmlChar *) palloc(pathsize + 9);
-       memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
        strncpy((char *) xpath, "string(", 7);
+       memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize);
        xpath[pathsize + 7] = ')';
        xpath[pathsize + 8] = '\0';
 
-       xpres = pgxml_result_to_text(pgxml_xpath(PG_GETARG_TEXT_P(0), xpath),
-                                                                NULL, NULL, NULL);
+       res = pgxml_xpath(document, xpath, &workspace);
+
+       xpres = pgxml_result_to_text(res, NULL, NULL, NULL);
+
+       cleanup_workspace(&workspace);
 
        pfree(xpath);
 
@@ -319,21 +329,17 @@ PG_FUNCTION_INFO_V1(xpath_number);
 Datum
 xpath_number(PG_FUNCTION_ARGS)
 {
+       text       *document = PG_GETARG_TEXT_P(0);
+       text       *xpathsupp = PG_GETARG_TEXT_P(1);    /* XPath expression */
        xmlChar    *xpath;
-       int32           pathsize;
-       text       *xpathsupp;
        float4          fRes;
-
        xmlXPathObjectPtr res;
-
-       /* PG_GETARG_TEXT_P(0) is document buffer */
-       xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
-
-       pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
+       xpath_workspace workspace;
 
        xpath = pgxml_texttoxmlchar(xpathsupp);
 
-       res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);
+       res = pgxml_xpath(document, xpath, &workspace);
+
        pfree(xpath);
 
        if (res == NULL)
@@ -341,6 +347,8 @@ xpath_number(PG_FUNCTION_ARGS)
 
        fRes = xmlXPathCastToNumber(res);
 
+       cleanup_workspace(&workspace);
+
        if (xmlXPathIsNaN(fRes))
                PG_RETURN_NULL();
 
@@ -353,21 +361,17 @@ PG_FUNCTION_INFO_V1(xpath_bool);
 Datum
 xpath_bool(PG_FUNCTION_ARGS)
 {
+       text       *document = PG_GETARG_TEXT_P(0);
+       text       *xpathsupp = PG_GETARG_TEXT_P(1);    /* XPath expression */
        xmlChar    *xpath;
-       int32           pathsize;
-       text       *xpathsupp;
        int                     bRes;
-
        xmlXPathObjectPtr res;
-
-       /* PG_GETARG_TEXT_P(0) is document buffer */
-       xpathsupp = PG_GETARG_TEXT_P(1);        /* XPath expression */
-
-       pathsize = VARSIZE(xpathsupp) - VARHDRSZ;
+       xpath_workspace workspace;
 
        xpath = pgxml_texttoxmlchar(xpathsupp);
 
-       res = pgxml_xpath(PG_GETARG_TEXT_P(0), xpath);
+       res = pgxml_xpath(document, xpath, &workspace);
+
        pfree(xpath);
 
        if (res == NULL)
@@ -375,6 +379,8 @@ xpath_bool(PG_FUNCTION_ARGS)
 
        bRes = xmlXPathCastToBoolean(res);
 
+       cleanup_workspace(&workspace);
+
        PG_RETURN_BOOL(bRes);
 }
 
@@ -383,49 +389,61 @@ xpath_bool(PG_FUNCTION_ARGS)
 /* Core function to evaluate XPath query */
 
 static xmlXPathObjectPtr
-pgxml_xpath(text *document, xmlChar *xpath)
+pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace)
 {
-       xmlDocPtr       doctree;
-       xmlXPathContextPtr ctxt;
+       int32           docsize = VARSIZE(document) - VARHDRSZ;
        xmlXPathObjectPtr res;
        xmlXPathCompExprPtr comppath;
-       int32           docsize;
 
-       docsize = VARSIZE(document) - VARHDRSZ;
+       workspace->doctree = NULL;
+       workspace->ctxt = NULL;
+       workspace->res = NULL;
 
        pgxml_parser_init();
 
-       doctree = xmlParseMemory((char *) VARDATA(document), docsize);
-       if (doctree == NULL)
+       workspace->doctree = xmlParseMemory((char *) VARDATA(document), docsize);
+       if (workspace->doctree == NULL)
                return NULL;                    /* not well-formed */
 
-       ctxt = xmlXPathNewContext(doctree);
-       ctxt->node = xmlDocGetRootElement(doctree);
+       workspace->ctxt = xmlXPathNewContext(workspace->doctree);
+       workspace->ctxt->node = xmlDocGetRootElement(workspace->doctree);
 
        /* compile the path */
        comppath = xmlXPathCompile(xpath);
        if (comppath == NULL)
        {
-               xmlFreeDoc(doctree);
+               cleanup_workspace(workspace);
                xml_ereport(ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION,
                                        "XPath Syntax Error");
        }
 
        /* Now evaluate the path expression. */
-       res = xmlXPathCompiledEval(comppath, ctxt);
+       res = xmlXPathCompiledEval(comppath, workspace->ctxt);
+       workspace->res = res;
+
        xmlXPathFreeCompExpr(comppath);
 
        if (res == NULL)
-       {
-               xmlXPathFreeContext(ctxt);
-               xmlFreeDoc(doctree);
+               cleanup_workspace(workspace);
 
-               return NULL;
-       }
-       /* xmlFreeDoc(doctree); */
        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 *
 pgxml_result_to_text(xmlXPathObjectPtr res,
                                         xmlChar *toptag,