diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 5fa5f0b..2dbab27 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -110,6 +110,8 @@ static int parse_xml_decl(const xmlChar * str, size_t *lenp,
xmlChar ** version, xmlChar ** encoding, int *standalone);
static bool print_xml_decl(StringInfo buf, const xmlChar * version,
pg_enc encoding, int standalone);
+static xmlDocPtr xml_parse_with_nodes(text *data, XmlOptionType xmloption_arg,
+ bool preserve_whitespace, xmlChar * encoding, xmlNodePtr *chunks);
static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
bool preserve_whitespace, xmlChar * encoding);
static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
@@ -1125,8 +1127,8 @@ print_xml_decl(StringInfo buf, const xmlChar * version,
* yet do not use SAX - see xmlreader.c)
*/
static xmlDocPtr
-xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
- xmlChar * encoding)
+xml_parse_with_nodes(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
+ xmlChar * encoding, xmlNodePtr *chunks)
{
int32 len;
xmlChar *string;
@@ -1185,7 +1187,7 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
res_code);
res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0,
- utf8string + count, NULL);
+ utf8string + count, chunks);
if (res_code != 0)
xml_ereport(ERROR, ERRCODE_INVALID_XML_CONTENT,
"invalid XML content");
@@ -1200,6 +1202,15 @@ xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
return doc;
}
+static xmlDocPtr
+xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace,
+ xmlChar * encoding)
+{
+ return xml_parse_with_nodes(
+ data, xmloption_arg, preserve_whitespace, encoding, NULL
+ );
+}
+
/*
* xmlChar<->text conversions
@@ -3174,6 +3185,30 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
}
#endif
+/*
+ * Provide access to the document's content fragment.
+ */
+static void
+xpathfun_fragment(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlNodeSetPtr nset = NULL;
+ xmlNodePtr cur;
+
+ if (nargs != 0)
+ {
+ xmlXPathSetArityError(ctxt);
+ return;
+ }
+
+ cur = ctxt->context->node;
+ while (cur != NULL)
+ {
+ nset = xmlXPathNodeSetMerge(nset, xmlXPathNodeSetCreate(cur));
+ cur = cur->next;
+ }
+
+ valuePush(ctxt, xmlXPathWrapNodeSet(nset));
+}
/*
* Evaluate XPath expression and return array of XML values.
@@ -3193,15 +3228,12 @@ xpath(PG_FUNCTION_ARGS)
xmltype *data = PG_GETARG_XML_P(1);
ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
ArrayBuildState *astate = NULL;
- xmlParserCtxtPtr ctxt;
xmlDocPtr doc;
+ xmlNodePtr chunks;
xmlXPathContextPtr xpathctx;
xmlXPathCompExprPtr xpathcomp;
xmlXPathObjectPtr xpathobj;
- char *datastr;
- int32 len;
int32 xpath_len;
- xmlChar *string;
xmlChar *xpath_expr;
int i;
int res_nitems;
@@ -3248,51 +3280,12 @@ xpath(PG_FUNCTION_ARGS)
ns_count = 0;
}
- datastr = VARDATA(data);
- len = VARSIZE(data) - VARHDRSZ;
- xpath_len = VARSIZE(xpath_expr_text) - VARHDRSZ;
- if (xpath_len == 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATA_EXCEPTION),
- errmsg("empty XPath expression")));
-
xml_init();
- /*
- * To handle both documents and fragments, regardless of the fact whether
- * the XML datum has a single root (XML well-formedness), we wrap the XML
- * datum in a dummy element (...) and extend the XPath expression
- * accordingly. To do it, throw away the XML prolog, if any.
- */
- if (len >= 5 &&
- xmlStrncmp((xmlChar *) datastr, (xmlChar *) "'))
- i++;
-
- if (i == len)
- xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
- "could not parse XML data");
-
- ++i;
-
- datastr += i;
- len -= i;
- }
-
- string = (xmlChar *) palloc((len + 8) * sizeof(xmlChar));
- memcpy(string, "", 3);
- memcpy(string + 3, datastr, len);
- memcpy(string + 3 + len, "", 5);
- len += 7;
-
- xpath_expr = (xmlChar *) palloc((xpath_len + 3) * sizeof(xmlChar));
- memcpy(xpath_expr, "/x", 2);
- memcpy(xpath_expr + 2, VARDATA(xpath_expr_text), xpath_len);
- xpath_expr[xpath_len + 2] = '\0';
- xpath_len += 2;
+ xpath_len = VARSIZE(xpath_expr_text) - VARHDRSZ;
+ xpath_expr = (xmlChar *) palloc((xpath_len + 1) * sizeof(xmlChar));
+ memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len);
+ xpath_expr[xpath_len] = '\0';
xmlInitParser();
@@ -3300,19 +3293,19 @@ xpath(PG_FUNCTION_ARGS)
* 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);
+ doc = xml_parse_with_nodes(data, xmloption, true, NULL, &chunks);
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);
+
+ xmlXPathRegisterFunc(xpathctx, (xmlChar *) "fragment", (xmlXPathFunction) xpathfun_fragment);
+
+ xpathctx->node = chunks;
if (xpathctx->node == NULL)
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
"could not find root XML element");
@@ -3376,7 +3369,6 @@ xpath(PG_FUNCTION_ARGS)
xmlXPathFreeObject(xpathobj);
xmlXPathFreeContext(xpathctx);
xmlFreeDoc(doc);
- xmlFreeParserCtxt(ctxt);
if (res_nitems == 0)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID));