against the XML value
<replaceable>xml</replaceable>. It returns an array of XML values
corresponding to the node set produced by the XPath expression.
+ If the XPath expression returns a scalar value rather than a node set,
+ a single-element array is returned.
</para>
<para>
static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg,
bool preserve_whitespace, int encoding);
static text *xml_xmlnodetoxmltype(xmlNodePtr cur);
+static int xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
+ ArrayBuildState **astate);
#endif /* USE_LIBXML */
static StringInfo query_to_xml_internal(const char *query, char *tablename,
*/
#ifdef USE_LIBXML
+
/*
* Convert XML node to text (dump subtree in case of element,
* return value otherwise)
return result;
}
-#endif
+
+/*
+ * Convert an XML XPath object (the result of evaluating an XPath expression)
+ * to an array of xml values, which is returned at *astate. The function
+ * result value is the number of elements in the array.
+ *
+ * If "astate" is NULL then we don't generate the array value, but we still
+ * return the number of elements it would have had.
+ *
+ * Nodesets are converted to an array containing the nodes' textual
+ * representations. Primitive values (float, double, string) are converted
+ * to a single-element array containing the value's string representation.
+ */
+static int
+xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj,
+ ArrayBuildState **astate)
+{
+ int result = 0;
+ Datum datum;
+ Oid datumtype;
+ char *result_str;
+
+ if (astate != NULL)
+ *astate = NULL;
+
+ switch (xpathobj->type)
+ {
+ case XPATH_NODESET:
+ if (xpathobj->nodesetval != NULL)
+ {
+ result = xpathobj->nodesetval->nodeNr;
+ if (astate != NULL)
+ {
+ int i;
+
+ for (i = 0; i < result; i++)
+ {
+ datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i]));
+ *astate = accumArrayResult(*astate, datum,
+ false, XMLOID,
+ CurrentMemoryContext);
+ }
+ }
+ }
+ return result;
+
+ case XPATH_BOOLEAN:
+ if (astate == NULL)
+ return 1;
+ datum = BoolGetDatum(xpathobj->boolval);
+ datumtype = BOOLOID;
+ break;
+
+ case XPATH_NUMBER:
+ if (astate == NULL)
+ return 1;
+ datum = Float8GetDatum(xpathobj->floatval);
+ datumtype = FLOAT8OID;
+ break;
+
+ case XPATH_STRING:
+ if (astate == NULL)
+ return 1;
+ datum = CStringGetDatum((char *) xpathobj->stringval);
+ datumtype = CSTRINGOID;
+ break;
+
+ default:
+ elog(ERROR, "xpath expression result type %d is unsupported",
+ xpathobj->type);
+ return 0; /* keep compiler quiet */
+ }
+
+ /* Common code for scalar-value cases */
+ result_str = map_sql_value_to_xml_value(datum, datumtype, true);
+ datum = PointerGetDatum(cstring_to_xmltype(result_str));
+ *astate = accumArrayResult(*astate, datum,
+ false, XMLOID,
+ CurrentMemoryContext);
+ return 1;
+}
/*
* Common code for xpath() and xmlexists()
*
* Evaluate XPath expression and return number of nodes in res_items
- * and array of XML values in astate.
+ * and array of XML values in astate. Either of those pointers can be
+ * NULL if the corresponding result isn't wanted.
*
* It is up to the user to ensure that the XML passed is in fact
* an XML document - XPath doesn't work easily on fragments without
* a context node being known.
*/
-#ifdef USE_LIBXML
static void
xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
int *res_nitems, ArrayBuildState **astate)
xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
"could not create XPath object");
- /* return empty array in cases when nothing is found */
- if (xpathobj->nodesetval == NULL)
- *res_nitems = 0;
+ /*
+ * Extract the results as requested.
+ */
+ if (res_nitems != NULL)
+ *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate);
else
- *res_nitems = xpathobj->nodesetval->nodeNr;
-
- if (*res_nitems && astate)
- {
- *astate = NULL;
- 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);
- }
- }
+ (void) xml_xpathobjtoxmlarray(xpathobj, astate);
}
PG_CATCH();
{
{<}
(1 row)
+SELECT xpath('''<<invalid>>''', '<root/>');
+ xpath
+---------------------------
+ {<<invalid>>}
+(1 row)
+
+SELECT xpath('count(//*)', '<root><sub/><sub/></root>');
+ xpath
+-------
+ {3}
+(1 row)
+
+SELECT xpath('count(//*)=0', '<root><sub/><sub/></root>');
+ xpath
+---------
+ {false}
+(1 row)
+
+SELECT xpath('count(//*)=3', '<root><sub/><sub/></root>');
+ xpath
+--------
+ {true}
+(1 row)
+
+SELECT xpath('name(/*)', '<root><sub/><sub/></root>');
+ xpath
+--------
+ {root}
+(1 row)
+
+SELECT xpath('/nosuchtag', '<root/>');
+ xpath
+-------
+ {}
+(1 row)
+
-- Test xmlexists and xpath_exists
SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>');
xmlexists
t
(1 row)
+SELECT xmlexists('count(/nosuchtag)' PASSING BY REF '<root/>');
+ xmlexists
+-----------
+ t
+(1 row)
+
SELECT xpath_exists('//town[text() = ''Toronto'']','<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>'::xml);
xpath_exists
--------------
t
(1 row)
+SELECT xpath_exists('count(/nosuchtag)', '<root/>'::xml);
+ xpath_exists
+--------------
+ t
+(1 row)
+
INSERT INTO xmltest VALUES (4, '<menu><beers><name>Budvar</name><cost>free</cost><name>Carling</name><cost>lots</cost></beers></menu>'::xml);
INSERT INTO xmltest VALUES (5, '<menu><beers><name>Molson</name><cost>free</cost><name>Carling</name><cost>lots</cost></beers></menu>'::xml);
INSERT INTO xmltest VALUES (6, '<myns:menu xmlns:myns="http://myns.com"><myns:beers><myns:name>Budvar</myns:name><myns:cost>free</myns:cost><myns:name>Carling</myns:name><myns:cost>lots</myns:cost></myns:beers></myns:menu>'::xml);
^
DETAIL: This functionality requires the server to be built with libxml support.
HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath('''<<invalid>>''', '<root/>');
+ERROR: unsupported XML feature
+LINE 1: SELECT xpath('''<<invalid>>''', '<root/>');
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath('count(//*)', '<root><sub/><sub/></root>');
+ERROR: unsupported XML feature
+LINE 1: SELECT xpath('count(//*)', '<root><sub/><sub/></root>');
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath('count(//*)=0', '<root><sub/><sub/></root>');
+ERROR: unsupported XML feature
+LINE 1: SELECT xpath('count(//*)=0', '<root><sub/><sub/></root>');
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath('count(//*)=3', '<root><sub/><sub/></root>');
+ERROR: unsupported XML feature
+LINE 1: SELECT xpath('count(//*)=3', '<root><sub/><sub/></root>');
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath('name(/*)', '<root><sub/><sub/></root>');
+ERROR: unsupported XML feature
+LINE 1: SELECT xpath('name(/*)', '<root><sub/><sub/></root>');
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath('/nosuchtag', '<root/>');
+ERROR: unsupported XML feature
+LINE 1: SELECT xpath('/nosuchtag', '<root/>');
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
-- Test xmlexists and xpath_exists
SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>');
ERROR: unsupported XML feature
^
DETAIL: This functionality requires the server to be built with libxml support.
HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT xmlexists('count(/nosuchtag)' PASSING BY REF '<root/>');
+ERROR: unsupported XML feature
+LINE 1: ...LECT xmlexists('count(/nosuchtag)' PASSING BY REF '<root/>')...
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
SELECT xpath_exists('//town[text() = ''Toronto'']','<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>'::xml);
ERROR: unsupported XML feature
LINE 1: ...ELECT xpath_exists('//town[text() = ''Toronto'']','<towns><t...
^
DETAIL: This functionality requires the server to be built with libxml support.
HINT: You need to rebuild PostgreSQL using --with-libxml.
+SELECT xpath_exists('count(/nosuchtag)', '<root/>'::xml);
+ERROR: unsupported XML feature
+LINE 1: SELECT xpath_exists('count(/nosuchtag)', '<root/>'::xml);
+ ^
+DETAIL: This functionality requires the server to be built with libxml support.
+HINT: You need to rebuild PostgreSQL using --with-libxml.
INSERT INTO xmltest VALUES (4, '<menu><beers><name>Budvar</name><cost>free</cost><name>Carling</name><cost>lots</cost></beers></menu>'::xml);
ERROR: unsupported XML feature
LINE 1: INSERT INTO xmltest VALUES (4, '<menu><beers><name>Budvar</n...
SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
SELECT xpath('//text()', '<root><</root>');
SELECT xpath('//@value', '<root value="<"/>');
+SELECT xpath('''<<invalid>>''', '<root/>');
+SELECT xpath('count(//*)', '<root><sub/><sub/></root>');
+SELECT xpath('count(//*)=0', '<root><sub/><sub/></root>');
+SELECT xpath('count(//*)=3', '<root><sub/><sub/></root>');
+SELECT xpath('name(/*)', '<root><sub/><sub/></root>');
+SELECT xpath('/nosuchtag', '<root/>');
-- Test xmlexists and xpath_exists
SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>');
SELECT xmlexists('//town[text() = ''Cwmbran'']' PASSING BY REF '<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>');
+SELECT xmlexists('count(/nosuchtag)' PASSING BY REF '<root/>');
SELECT xpath_exists('//town[text() = ''Toronto'']','<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>'::xml);
SELECT xpath_exists('//town[text() = ''Cwmbran'']','<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>'::xml);
+SELECT xpath_exists('count(/nosuchtag)', '<root/>'::xml);
INSERT INTO xmltest VALUES (4, '<menu><beers><name>Budvar</name><cost>free</cost><name>Carling</name><cost>lots</cost></beers></menu>'::xml);
INSERT INTO xmltest VALUES (5, '<menu><beers><name>Molson</name><cost>free</cost><name>Carling</name><cost>lots</cost></beers></menu>'::xml);