diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 28b3eaaa20..41145e697a 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -3720,35 +3720,58 @@ xml_xmlnodetoxmltype(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt) if (cur->type != XML_ATTRIBUTE_NODE && cur->type != XML_TEXT_NODE) { - xmlBufferPtr buf; - xmlNodePtr cur_copy; + volatile xmlBufferPtr buf = NULL; + volatile xmlNodePtr cur_copy = NULL; - buf = xmlBufferCreate(); + PG_TRY(); + { + int bytes; - /* - * The result of xmlNodeDump() won't contain namespace definitions - * from parent nodes, but xmlCopyNode() duplicates a node along with - * its required namespace definitions. - */ - cur_copy = xmlCopyNode(cur, 1); + buf = xmlBufferCreate(); - if (cur_copy == NULL) - xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, - "could not copy node"); + if (buf == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate xmlBuffer"); + + /* + * The result of xmlNodeDump() won't contain namespace definitions + * from parent nodes, but xmlCopyNode() duplicates a node along with + * its required namespace definitions. + * + * Some older libxml2 like 2.7.6 produces partialy broken + * XML_DOCUMENT_NODE nodes (unset content field). The copy of this node + * is ok, but xmlFreeNode fails. Fortunately, XML_DOCUMENT_NODE should + * not to have parent nodes, and we don't need to use xmlCopyNode. + */ + if (cur->type != XML_DOCUMENT_NODE) + { + cur_copy = xmlCopyNode(cur, 1); + if (cur == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "could not copy node"); + } + else + cur_copy = cur; + + bytes = xmlNodeDump(buf, NULL, cur_copy, 0, 1); + if (bytes == -1 || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "could not dump node"); - PG_TRY(); - { - xmlNodeDump(buf, NULL, cur_copy, 0, 1); result = xmlBuffer_to_xmltype(buf); } PG_CATCH(); { - xmlFreeNode(cur_copy); - xmlBufferFree(buf); + if (cur_copy && cur_copy != cur) + xmlFreeNode(cur_copy); + if (buf) + xmlBufferFree(buf); PG_RE_THROW(); } PG_END_TRY(); - xmlFreeNode(cur_copy); + + if (cur_copy != cur) + xmlFreeNode(cur_copy); xmlBufferFree(buf); } else