From ac12412edec900fea429d5b9bf9e58990d9f4533 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 15 Jan 2008 18:57:00 +0000 Subject: [PATCH] Revise memory management for libxml calls. Instead of keeping libxml's data in whichever context happens to be current during a call of an xml.c function, use a dedicated context that will not go away until we explicitly delete it (which we do at transaction end or subtransaction abort). This makes recovery after an error much simpler --- we don't have to individually delete the data structures created by libxml. Also, we need to initialize and cleanup libxml only once per transaction (if there's no error) instead of once per function call, so it should be a bit faster. We'll need to keep an eye out for intra-transaction memory leaks, though. Alvaro and Tom. --- src/backend/access/transam/xact.c | 7 +- src/backend/utils/adt/xml.c | 500 +++++++++++++++--------------- src/include/utils/xml.h | 4 +- 3 files changed, 254 insertions(+), 257 deletions(-) diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 4cebea05c0..13c3a0378b 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.256 2008/01/03 21:23:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.257 2008/01/15 18:56:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,6 +45,7 @@ #include "utils/inval.h" #include "utils/memutils.h" #include "utils/relcache.h" +#include "utils/xml.h" /* @@ -1671,6 +1672,7 @@ CommitTransaction(void) AtEOXact_GUC(true, 1); AtEOXact_SPI(true); + AtEOXact_xml(); AtEOXact_on_commit_actions(true); AtEOXact_Namespace(true); /* smgrcommit already done */ @@ -1880,6 +1882,7 @@ PrepareTransaction(void) /* PREPARE acts the same as COMMIT as far as GUC is concerned */ AtEOXact_GUC(true, 1); AtEOXact_SPI(true); + AtEOXact_xml(); AtEOXact_on_commit_actions(true); AtEOXact_Namespace(true); /* smgrcommit already done */ @@ -2021,6 +2024,7 @@ AbortTransaction(void) AtEOXact_GUC(false, 1); AtEOXact_SPI(false); + AtEOXact_xml(); AtEOXact_on_commit_actions(false); AtEOXact_Namespace(false); smgrabort(); @@ -3851,6 +3855,7 @@ AbortSubTransaction(void) AtEOXact_GUC(false, s->gucNestLevel); AtEOSubXact_SPI(false, s->subTransactionId); + AtEOXact_xml(); AtEOSubXact_on_commit_actions(false, s->subTransactionId, s->parent->subTransactionId); AtEOSubXact_Namespace(false, s->subTransactionId, diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 411cd6c22e..0dc05f0d34 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.67 2008/01/12 21:14:08 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.68 2008/01/15 18:56:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,18 +24,30 @@ */ /* - * Note on memory management: Via callbacks, libxml is told to use - * palloc and friends for memory management. Sometimes, libxml - * allocates global structures in the hope that it can reuse them - * later on, but if "later" is much later, the memory context - * management of PostgreSQL will have blown those structures away - * without telling libxml about it. Therefore, it is important to - * call xmlCleanupParser() or perhaps some other cleanup function - * after using such functions, for example something from - * libxml/parser.h or libxml/xmlsave.h. Unfortunately, you cannot - * readily tell from the API documentation when that happens, so - * careful evaluation is necessary when introducing new libxml APIs - * here. + * Notes on memory management: + * + * Via callbacks, libxml is told to use palloc and friends for memory + * management, within a context that we reset at transaction end (and also at + * subtransaction abort) to prevent memory leaks. Resetting at transaction or + * subtransaction abort is necessary since we might have thrown a longjmp + * while some data structures were not linked from anywhere persistent. + * Resetting at transaction commit might not be necessary, but seems a good + * idea to forestall long-term leaks. + * + * Sometimes libxml allocates global structures in the hope that it can reuse + * them later on. Therefore, before resetting LibxmlContext, we must tell + * libxml to discard any global data it has. The libxml API documentation is + * not very good about specifying this, but for now we assume that + * xmlCleanupParser() will get rid of anything we need to worry about. + * + * We use palloc --- which will throw a longjmp on error --- for allocation + * callbacks that officially should act like malloc, ie, return NULL on + * out-of-memory. This is a bit risky since there is a chance of leaving + * persistent libxml data structures in an inconsistent partially-constructed + * state, perhaps leading to crash in xmlCleanupParser(). However, as of + * early 2008 it is *known* that libxml can crash on out-of-memory due to + * inadequate checks for NULL returns, so this behavior seems the lesser + * of two evils. */ #include "postgres.h" @@ -80,8 +92,11 @@ XmlOptionType xmloption; #ifdef USE_LIBXML static StringInfo xml_err_buf = NULL; +static MemoryContext LibxmlContext = NULL; static void xml_init(void); +static void xml_memory_init(void); +static void xml_memory_cleanup(void); static void *xml_palloc(size_t size); static void *xml_repalloc(void *ptr, size_t size); static void xml_pfree(void *ptr); @@ -784,84 +799,53 @@ xmlvalidate(PG_FUNCTION_ARGS) text *data = PG_GETARG_TEXT_P(0); text *dtdOrUri = PG_GETARG_TEXT_P(1); bool result = false; - xmlParserCtxtPtr ctxt = NULL; - xmlDocPtr doc = NULL; - xmlDtdPtr dtd = NULL; + xmlParserCtxtPtr ctxt; + xmlDocPtr doc; + xmlDtdPtr dtd; xml_init(); + xmlInitParser(); + ctxt = xmlNewParserCtxt(); + if (ctxt == NULL) + xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate parser context"); - /* We use a PG_TRY block to ensure libxml parser is cleaned up on error */ - PG_TRY(); - { - xmlInitParser(); - ctxt = xmlNewParserCtxt(); - if (ctxt == NULL) - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, - "could not allocate parser context"); - - doc = xmlCtxtReadMemory(ctxt, (char *) VARDATA(data), - VARSIZE(data) - VARHDRSZ, - NULL, NULL, 0); - if (doc == NULL) - xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, - "could not parse XML data"); + doc = xmlCtxtReadMemory(ctxt, (char *) VARDATA(data), + VARSIZE(data) - VARHDRSZ, + NULL, NULL, 0); + if (doc == NULL) + xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, + "could not parse XML data"); #if 0 - uri = xmlCreateURI(); - elog(NOTICE, "dtd - %s", dtdOrUri); - dtd = palloc(sizeof(xmlDtdPtr)); - uri = xmlParseURI(dtdOrUri); - if (uri == NULL) - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, - "not implemented yet... (TODO)"); - else + uri = xmlCreateURI(); + elog(NOTICE, "dtd - %s", dtdOrUri); + dtd = palloc(sizeof(xmlDtdPtr)); + uri = xmlParseURI(dtdOrUri); + if (uri == NULL) + xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, + "not implemented yet... (TODO)"); + else #endif - dtd = xmlParseDTD(NULL, xml_text2xmlChar(dtdOrUri)); + dtd = xmlParseDTD(NULL, xml_text2xmlChar(dtdOrUri)); - if (dtd == NULL) - xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, - "could not load DTD"); + if (dtd == NULL) + xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, + "could not load DTD"); - if (xmlValidateDtd(xmlNewValidCtxt(), doc, dtd) == 1) - result = true; + if (xmlValidateDtd(xmlNewValidCtxt(), doc, dtd) == 1) + result = true; - if (!result) - xml_ereport(NOTICE, ERRCODE_INVALID_XML_DOCUMENT, - "validation against DTD failed"); + if (!result) + xml_ereport(NOTICE, ERRCODE_INVALID_XML_DOCUMENT, + "validation against DTD failed"); #if 0 - if (uri) - xmlFreeURI(uri); - uri = NULL; + xmlFreeURI(uri); #endif - if (dtd) - xmlFreeDtd(dtd); - dtd = NULL; - if (doc) - xmlFreeDoc(doc); - doc = NULL; - if (ctxt) - xmlFreeParserCtxt(ctxt); - ctxt = NULL; - xmlCleanupParser(); - } - PG_CATCH(); - { -#if 0 - if (uri) - xmlFreeURI(uri); -#endif - if (dtd) - xmlFreeDtd(dtd); - if (doc) - xmlFreeDoc(doc); - if (ctxt) - xmlFreeParserCtxt(ctxt); - xmlCleanupParser(); - - PG_RE_THROW(); - } - PG_END_TRY(); + xmlFreeDtd(dtd); + xmlFreeDoc(doc); + xmlFreeParserCtxt(ctxt); PG_RETURN_BOOL(result); #else /* not USE_LIBXML */ @@ -915,6 +899,19 @@ xml_is_document(xmltype *arg) } +/* + * xml cleanup function for transaction end. This is also called on + * subtransaction abort; see notes at top of file for rationale. + */ +void +AtEOXact_xml(void) +{ +#ifdef USE_LIBXML + xml_memory_cleanup(); +#endif +} + + #ifdef USE_LIBXML /* @@ -953,14 +950,11 @@ xml_init(void) xmlSetGenericErrorFunc(NULL, xml_errorHandler); /* Set up memory allocation our way, too */ - xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup); + xml_memory_init(); /* Check library compatibility */ LIBXML_TEST_VERSION; - /* The above calls xmlInitParser(); must clean up dangling pointers */ - xmlCleanupParser(); - first_time = false; } else @@ -977,7 +971,7 @@ xml_init(void) * about, anyway. */ xmlSetGenericErrorFunc(NULL, xml_errorHandler); - xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup); + xml_memory_init(); } } @@ -1210,7 +1204,7 @@ print_xml_decl(StringInfo buf, const xmlChar * version, * Convert a C string to XML internal representation * * TODO maybe, libxml2's xmlreader is better? (do not construct DOM, - * yet do not use SAX - see xml_reader.c) + * yet do not use SAX - see xmlreader.c) */ static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, @@ -1219,8 +1213,8 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, int32 len; xmlChar *string; xmlChar *utf8string; - xmlParserCtxtPtr ctxt = NULL; - xmlDocPtr doc = NULL; + xmlParserCtxtPtr ctxt; + xmlDocPtr doc; len = VARSIZE(data) - VARHDRSZ; /* will be useful later */ string = xml_text2xmlChar(data); @@ -1233,73 +1227,57 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, PG_UTF8); xml_init(); + xmlInitParser(); + ctxt = xmlNewParserCtxt(); + if (ctxt == NULL) + xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate parser context"); - /* We use a PG_TRY block to ensure libxml parser is cleaned up on error */ - PG_TRY(); + if (xmloption_arg == XMLOPTION_DOCUMENT) { - xmlInitParser(); - ctxt = xmlNewParserCtxt(); - if (ctxt == NULL) - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, - "could not allocate parser context"); - - if (xmloption_arg == XMLOPTION_DOCUMENT) - { - /* - * Note, that here we try to apply DTD defaults - * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d: 'Default - * valies defined by internal DTD are applied'. As for external - * DTDs, we try to support them too, (see SQL/XML:10.16.7.e) - */ - doc = xmlCtxtReadDoc(ctxt, utf8string, - NULL, - "UTF-8", - XML_PARSE_NOENT | XML_PARSE_DTDATTR - | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS)); - if (doc == NULL) - xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, - "invalid XML document"); - } - else - { - int res_code; - size_t count; - xmlChar *version = NULL; - int standalone = -1; - - doc = xmlNewDoc(NULL); - - res_code = parse_xml_decl(utf8string, &count, &version, NULL, &standalone); - if (res_code != 0) - xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT, - "invalid XML content: invalid XML declaration", res_code); - - res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, utf8string + count, NULL); - if (res_code != 0) - xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT, - "invalid XML content"); - - doc->version = xmlStrdup(version); - doc->encoding = xmlStrdup((xmlChar *) "UTF-8"); - doc->standalone = standalone; - } - - if (ctxt) - xmlFreeParserCtxt(ctxt); - ctxt = NULL; - xmlCleanupParser(); + /* + * Note, that here we try to apply DTD defaults + * (XML_PARSE_DTDATTR) according to SQL/XML:10.16.7.d: 'Default + * values defined by internal DTD are applied'. As for external + * DTDs, we try to support them too, (see SQL/XML:10.16.7.e) + */ + doc = xmlCtxtReadDoc(ctxt, utf8string, + NULL, + "UTF-8", + XML_PARSE_NOENT | XML_PARSE_DTDATTR + | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS)); + if (doc == NULL) + xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, + "invalid XML document"); } - PG_CATCH(); + else { - if (doc) - xmlFreeDoc(doc); - if (ctxt) - xmlFreeParserCtxt(ctxt); - xmlCleanupParser(); + int res_code; + size_t count; + xmlChar *version = NULL; + int standalone = -1; - PG_RE_THROW(); + doc = xmlNewDoc(NULL); + + res_code = parse_xml_decl(utf8string, + &count, &version, NULL, &standalone); + if (res_code != 0) + xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT, + "invalid XML content: invalid XML declaration", + res_code); + + res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, + utf8string + count, NULL); + if (res_code != 0) + xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT, + "invalid XML content"); + + doc->version = xmlStrdup(version); + doc->encoding = xmlStrdup((xmlChar *) "UTF-8"); + doc->standalone = standalone; } - PG_END_TRY(); + + xmlFreeParserCtxt(ctxt); return doc; } @@ -1322,13 +1300,50 @@ xml_text2xmlChar(text *in) } +/* + * Manage the special context used for all libxml allocations + */ +static void +xml_memory_init(void) +{ + /* + * Create memory context if not there already. We make it a child of + * TopMemoryContext, even though our current policy is that it doesn't + * survive past transaction end, because we want to be really really + * sure it doesn't go away before we've called xmlCleanupParser(). + */ + if (LibxmlContext == NULL) + LibxmlContext = AllocSetContextCreate(TopMemoryContext, + "LibxmlContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + /* Re-establish the callbacks even if already set */ + xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup); +} + +static void +xml_memory_cleanup(void) +{ + if (LibxmlContext != NULL) + { + /* Give libxml a chance to clean up dangling pointers */ + xmlCleanupParser(); + + /* And flush the context */ + MemoryContextDelete(LibxmlContext); + LibxmlContext = NULL; + } +} + /* * Wrappers for memory management functions */ static void * xml_palloc(size_t size) { - return palloc(size); + return MemoryContextAlloc(LibxmlContext, size); } @@ -1349,7 +1364,7 @@ xml_pfree(void *ptr) static char * xml_pstrdup(const char *string) { - return pstrdup(string); + return MemoryContextStrdup(LibxmlContext, string); } @@ -3262,11 +3277,11 @@ xpath(PG_FUNCTION_ARGS) xmltype *data = PG_GETARG_XML_P(1); ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2); ArrayBuildState *astate = NULL; - xmlParserCtxtPtr ctxt = NULL; - xmlDocPtr doc = NULL; - xmlXPathContextPtr xpathctx = NULL; - xmlXPathCompExprPtr xpathcomp = NULL; - xmlXPathObjectPtr xpathobj = NULL; + xmlParserCtxtPtr ctxt; + xmlDocPtr doc; + xmlXPathContextPtr xpathctx; + xmlXPathCompExprPtr xpathcomp; + xmlXPathObjectPtr xpathobj; char *datastr; int32 len; int32 xpath_len; @@ -3363,114 +3378,89 @@ xpath(PG_FUNCTION_ARGS) xpath_expr[xpath_len + 2] = '\0'; xpath_len += 2; - /* We use a PG_TRY block to ensure libxml parser is cleaned up on error */ - PG_TRY(); + xmlInitParser(); + + /* + * redundant XML parsing (two parsings for the same value during one + * command execution are possible) + */ + ctxt = xmlNewParserCtxt(); + if (ctxt == NULL) + xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate parser context"); + doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, NULL, 0); + if (doc == NULL) + xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, + "could not parse XML data"); + xpathctx = xmlXPathNewContext(doc); + if (xpathctx == NULL) + xml_ereport(ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate XPath context"); + xpathctx->node = xmlDocGetRootElement(doc); + if (xpathctx->node == NULL) + xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, + "could not find root XML element"); + + /* register namespaces, if any */ + if (ns_count > 0) { - xmlInitParser(); - - /* - * redundant XML parsing (two parsings for the same value during one - * command execution are possible) - */ - ctxt = xmlNewParserCtxt(); - if (ctxt == NULL) - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, - "could not allocate parser context"); - doc = xmlCtxtReadMemory(ctxt, (char *) string, len, NULL, NULL, 0); - if (doc == NULL) - xml_ereport(ERROR, ERRCODE_INVALID_XML_DOCUMENT, - "could not parse XML data"); - xpathctx = xmlXPathNewContext(doc); - if (xpathctx == NULL) - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, - "could not allocate XPath context"); - xpathctx->node = xmlDocGetRootElement(doc); - if (xpathctx->node == NULL) - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, - "could not find root XML element"); - - /* register namespaces, if any */ - if (ns_count > 0) + for (i = 0; i < ns_count; i++) { - for (i = 0; i < ns_count; i++) - { - char *ns_name; - char *ns_uri; + char *ns_name; + char *ns_uri; - if (ns_names_uris_nulls[i * 2] || - ns_names_uris_nulls[i * 2 + 1]) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("neither namespace name nor URI may be null"))); - ns_name = _textout(ns_names_uris[i * 2]); - ns_uri = _textout(ns_names_uris[i * 2 + 1]); - if (xmlXPathRegisterNs(xpathctx, - (xmlChar *) ns_name, - (xmlChar *) ns_uri) != 0) - ereport(ERROR, /* is this an internal error??? */ - (errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"", - ns_name, ns_uri))); - } + if (ns_names_uris_nulls[i * 2] || + ns_names_uris_nulls[i * 2 + 1]) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("neither namespace name nor URI may be null"))); + ns_name = _textout(ns_names_uris[i * 2]); + ns_uri = _textout(ns_names_uris[i * 2 + 1]); + if (xmlXPathRegisterNs(xpathctx, + (xmlChar *) ns_name, + (xmlChar *) ns_uri) != 0) + ereport(ERROR, /* is this an internal error??? */ + (errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"", + ns_name, ns_uri))); } - - xpathcomp = xmlXPathCompile(xpath_expr); - if (xpathcomp == NULL) /* TODO: show proper XPath error details */ - xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, - "invalid XPath expression"); - - xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx); - if (xpathobj == NULL) /* TODO: reason? */ - ereport(ERROR, - (errmsg("could not create XPath object"))); - - xmlXPathFreeCompExpr(xpathcomp); - xpathcomp = NULL; - - /* return empty array in cases when nothing is found */ - if (xpathobj->nodesetval == NULL) - res_nitems = 0; - else - res_nitems = xpathobj->nodesetval->nodeNr; - - if (res_nitems) - for (i = 0; i < xpathobj->nodesetval->nodeNr; i++) - { - Datum elem; - bool elemisnull = false; - - elem = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i])); - astate = accumArrayResult(astate, elem, - elemisnull, XMLOID, - CurrentMemoryContext); - } - - xmlXPathFreeObject(xpathobj); - xpathobj = NULL; - xmlXPathFreeContext(xpathctx); - xpathctx = NULL; - xmlFreeDoc(doc); - doc = NULL; - xmlFreeParserCtxt(ctxt); - ctxt = NULL; - xmlCleanupParser(); } - PG_CATCH(); + + xpathcomp = xmlXPathCompile(xpath_expr); + if (xpathcomp == NULL) /* TODO: show proper XPath error details */ + xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, + "invalid XPath expression"); + + xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx); + if (xpathobj == NULL) /* TODO: reason? */ + ereport(ERROR, + (errmsg("could not create XPath object"))); + + xmlXPathFreeCompExpr(xpathcomp); + + /* return empty array in cases when nothing is found */ + if (xpathobj->nodesetval == NULL) + res_nitems = 0; + else + res_nitems = xpathobj->nodesetval->nodeNr; + + if (res_nitems) { - if (xpathcomp) - xmlXPathFreeCompExpr(xpathcomp); - if (xpathobj) - xmlXPathFreeObject(xpathobj); - if (xpathctx) - xmlXPathFreeContext(xpathctx); - if (doc) - xmlFreeDoc(doc); - if (ctxt) - xmlFreeParserCtxt(ctxt); - xmlCleanupParser(); + for (i = 0; i < xpathobj->nodesetval->nodeNr; i++) + { + Datum elem; + bool elemisnull = false; - PG_RE_THROW(); + elem = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i])); + astate = accumArrayResult(astate, elem, + elemisnull, XMLOID, + CurrentMemoryContext); + } } - PG_END_TRY(); + + xmlXPathFreeObject(xpathobj); + xmlXPathFreeContext(xpathctx); + xmlFreeDoc(doc); + xmlFreeParserCtxt(ctxt); if (res_nitems == 0) PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID)); diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index 8d5b222f80..99a46f8ef7 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.22 2008/01/01 19:45:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.23 2008/01/15 18:57:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -75,6 +75,8 @@ extern char *map_sql_identifier_to_xml_name(char *ident, bool fully_escaped, boo extern char *map_xml_name_to_sql_identifier(char *name); extern char *map_sql_value_to_xml_value(Datum value, Oid type); +extern void AtEOXact_xml(void); + typedef enum { XMLBINARY_BASE64,