diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 702b9e3..105d2ac 100644 *** a/src/backend/utils/adt/xml.c --- b/src/backend/utils/adt/xml.c *************** static bool print_xml_decl(StringInfo bu *** 109,114 **** --- 109,116 ---- 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, *************** xml_xmlnodetoxmltype(xmlNodePtr cur) *** 3293,3298 **** --- 3295,3391 ---- return result; } + + /* + * Convert XML XPath object (the result of evaluating a XPath expression) + * to an array of xml values. Nodesets are converted to an array containg + * the node's textual representation. Primitive values (float, double, string) + * are converted to a single-element array containg the value's string + * representation. + */ + static int + xml_xpathobjtoxmlarray(xmlXPathObjectPtr xpathobj, + ArrayBuildState **astate) + { + int i; + Datum datum; + char* result_str; + + if (astate != NULL) + *astate = NULL; + + switch (xpathobj->type) { + /* For node sets, we append all the node's textual representations + * to the array + */ + case XPATH_NODESET: + if (xpathobj->nodesetval != NULL) + { + int nitems = 0; + + for (i = 0; i < xpathobj->nodesetval->nodeNr; i++) + { + ++nitems; + if (astate == NULL) + continue; + + datum = PointerGetDatum(xml_xmlnodetoxmltype(xpathobj->nodesetval->nodeTab[i])); + *astate = accumArrayResult(*astate, datum, + false, XMLOID, + CurrentMemoryContext); + } + + return nitems; + } + else + { + return 0; + } + + /* For scalar values, we encode the value in XML and return a + * single-element array + */ + + case XPATH_BOOLEAN: + if (astate == NULL) + return 1; + + datum = BoolGetDatum(xpathobj->boolval); + result_str = map_sql_value_to_xml_value(datum, BOOLOID, true); + goto single; + + case XPATH_NUMBER: + if (astate == NULL) + return 1; + + datum = Float8GetDatum(xpathobj->floatval); + result_str = map_sql_value_to_xml_value(datum, FLOAT8OID, true); + goto single; + + case XPATH_STRING: + if (astate == NULL) + return 1; + + datum = CStringGetDatum((char *) xpathobj->stringval); + result_str = map_sql_value_to_xml_value(datum, CSTRINGOID, true); + goto single; + + single: + datum = PointerGetDatum(cstring_to_xmltype(result_str)); + *astate = accumArrayResult(*astate, datum, + false, XMLOID, + CurrentMemoryContext); + return 1; + + default: + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("xpath expression result type %d is unsupported", + xpathobj->type))); + + return 0; /* Prevent compiler warning */ + } + } #endif *************** xpath_internal(text *xpath_expr_text, xm *** 3447,3473 **** if (xpathobj == NULL) /* TODO: reason? */ xml_ereport(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; 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); ! } ! } } PG_CATCH(); { --- 3540,3550 ---- if (xpathobj == NULL) /* TODO: reason? */ xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR, "could not create XPath object"); ! ! if (res_nitems != NULL) ! *res_nitems = xml_xpathobjtoxmlarray(xpathobj, astate); else ! xml_xpathobjtoxmlarray(xpathobj, astate); } PG_CATCH(); { diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index eaa5a74..d279522 100644 *** a/src/test/regress/expected/xml.out --- b/src/test/regress/expected/xml.out *************** SELECT xpath('//b', 'one two t *** 502,507 **** --- 502,543 ---- {two,etc} (1 row) + SELECT xpath('''<>''', ''); + xpath + --------------------------- + {<<invalid>>} + (1 row) + + SELECT xpath('count(//*)', ''); + xpath + ------- + {3} + (1 row) + + SELECT xpath('count(//*)=0', ''); + xpath + --------- + {false} + (1 row) + + SELECT xpath('count(//*)=3', ''); + xpath + -------- + {true} + (1 row) + + SELECT xpath('name(/*)', ''); + xpath + -------- + {root} + (1 row) + + SELECT xpath('/nosuchtag', ''); + xpath + ------- + {} + (1 row) + -- Test xmlexists and xpath_exists SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'); xmlexists *************** SELECT xmlexists('//town[text() = ''Cwmb *** 515,520 **** --- 551,562 ---- t (1 row) + SELECT xmlexists('count(/nosuchtag)' PASSING BY REF ''); + xmlexists + ----------- + t + (1 row) + SELECT xpath_exists('//town[text() = ''Toronto'']','Bidford-on-AvonCwmbranBristol'::xml); xpath_exists -------------- *************** SELECT xpath_exists('//town[text() = ''C *** 527,532 **** --- 569,580 ---- t (1 row) + SELECT xpath_exists('count(/nosuchtag)', ''::xml); + xpath_exists + -------------- + t + (1 row) + INSERT INTO xmltest VALUES (4, 'BudvarfreeCarlinglots'::xml); INSERT INTO xmltest VALUES (5, 'MolsonfreeCarlinglots'::xml); INSERT INTO xmltest VALUES (6, 'BudvarfreeCarlinglots'::xml); diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index 711b435..dac98d2 100644 *** a/src/test/regress/expected/xml_1.out --- b/src/test/regress/expected/xml_1.out *************** LINE 1: SELECT xpath('//b', 'one t *** 456,461 **** --- 456,497 ---- ^ DETAIL: This functionality requires the server to be built with libxml support. HINT: You need to rebuild PostgreSQL using --with-libxml. + SELECT xpath('''<>''', ''); + ERROR: unsupported XML feature + LINE 1: SELECT xpath('''<>''', ''); + ^ + DETAIL: This functionality requires the server to be built with libxml support. + HINT: You need to rebuild PostgreSQL using --with-libxml. + SELECT xpath('count(//*)', ''); + ERROR: unsupported XML feature + LINE 1: SELECT xpath('count(//*)', ''); + ^ + 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', ''); + ERROR: unsupported XML feature + LINE 1: SELECT xpath('count(//*)=0', ''); + ^ + 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', ''); + ERROR: unsupported XML feature + LINE 1: SELECT xpath('count(//*)=3', ''); + ^ + DETAIL: This functionality requires the server to be built with libxml support. + HINT: You need to rebuild PostgreSQL using --with-libxml. + SELECT xpath('name(/*)', ''); + ERROR: unsupported XML feature + LINE 1: SELECT xpath('name(/*)', ''); + ^ + DETAIL: This functionality requires the server to be built with libxml support. + HINT: You need to rebuild PostgreSQL using --with-libxml. + SELECT xpath('/nosuchtag', ''); + ERROR: unsupported XML feature + LINE 1: SELECT xpath('/nosuchtag', ''); + ^ + 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 'Bidford-on-AvonCwmbranBristol'); ERROR: unsupported XML feature *************** LINE 1: ...sts('//town[text() = ''Cwmbra *** 469,474 **** --- 505,516 ---- ^ 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 ''); + ERROR: unsupported XML feature + LINE 1: ...LECT xmlexists('count(/nosuchtag)' PASSING BY REF '')... + ^ + 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'']','Bidford-on-AvonCwmbranBristol'::xml); ERROR: unsupported XML feature LINE 1: ...ELECT xpath_exists('//town[text() = ''Toronto'']',''::xml); + ERROR: unsupported XML feature + LINE 1: SELECT xpath_exists('count(/nosuchtag)', ''::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, 'BudvarfreeCarlinglots'::xml); ERROR: unsupported XML feature LINE 1: INSERT INTO xmltest VALUES (4, 'Budvar'); *** 163,174 **** --- 163,182 ---- SELECT xpath('//text()', 'number one'); SELECT xpath('//loc:piece/@id', 'number one', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); SELECT xpath('//b', 'one two three etc'); + SELECT xpath('''<>''', ''); + SELECT xpath('count(//*)', ''); + SELECT xpath('count(//*)=0', ''); + SELECT xpath('count(//*)=3', ''); + SELECT xpath('name(/*)', ''); + SELECT xpath('/nosuchtag', ''); -- Test xmlexists and xpath_exists SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'); SELECT xmlexists('//town[text() = ''Cwmbran'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'); + SELECT xmlexists('count(/nosuchtag)' PASSING BY REF ''); SELECT xpath_exists('//town[text() = ''Toronto'']','Bidford-on-AvonCwmbranBristol'::xml); SELECT xpath_exists('//town[text() = ''Cwmbran'']','Bidford-on-AvonCwmbranBristol'::xml); + SELECT xpath_exists('count(/nosuchtag)', ''::xml); INSERT INTO xmltest VALUES (4, 'BudvarfreeCarlinglots'::xml); INSERT INTO xmltest VALUES (5, 'MolsonfreeCarlinglots'::xml);