From 4dfc4578548ea8f1bf69da5424d1e76a0b110a75 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Sun, 8 Aug 2010 19:15:27 +0000 Subject: [PATCH] Add an xpath_exists() function. This is equivalent to XMLEXISTS except that it offers support for namespace mapping. Mike Fowler, reviewed by David Fetter --- doc/src/sgml/func.sgml | 92 +++++++++++++++++++---------- src/backend/utils/adt/xml.c | 26 +++++++- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_proc.h | 7 ++- src/include/utils/xml.h | 3 +- src/test/regress/expected/xml.out | 50 +++++++++++++++- src/test/regress/expected/xml_1.out | 50 +++++++++++++++- src/test/regress/sql/xml.sql | 12 +++- 8 files changed, 205 insertions(+), 39 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index b981ca7f920..57b6f7aaa2a 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.524 2010/08/05 18:21:17 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.525 2010/08/08 19:15:27 tgl Exp $ --> <chapter id="functions"> <title>Functions and Operators</title> @@ -8175,7 +8175,7 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple linkend="datatype-xml"> for information about the <type>xml</type> type. The function-like expressions <function>xmlparse</function> and <function>xmlserialize</function> for converting to and from - type <type>xml</type> are not repeated here. Use of many of these + type <type>xml</type> are not repeated here. Use of most of these functions requires the installation to have been built with <command>configure --with-libxml</>. </para> @@ -8231,7 +8231,7 @@ SELECT xmlcomment('hello'); <synopsis> <function>xmlconcat</function>(<replaceable>xml</replaceable><optional>, ...</optional>) </synopsis> - + <para> The function <function>xmlconcat</function> concatenates a list of individual XML values to create a single value containing an @@ -8277,18 +8277,18 @@ SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone= ]]></screen> </para> </sect3> - + <sect3> <title><literal>xmlelement</literal></title> - + <indexterm> <primary>xmlelement</primary> </indexterm> - + <synopsis> <function>xmlelement</function>(name <replaceable>name</replaceable> <optional>, xmlattributes(<replaceable>value</replaceable> <optional>AS <replaceable>attname</replaceable></optional> <optional>, ... </optional>)</optional> <optional><replaceable>, content, ...</replaceable></optional>) </synopsis> - + <para> The <function>xmlelement</function> expression produces an XML element with the given name, attributes, and content. @@ -8375,18 +8375,18 @@ SELECT xmlelement(name foo, xmlattributes('xyz' as bar), at which point a more precise description will appear. </para> </sect3> - + <sect3> <title><literal>xmlforest</literal></title> - + <indexterm> <primary>xmlforest</primary> </indexterm> - + <synopsis> <function>xmlforest</function>(<replaceable>content</replaceable> <optional>AS <replaceable>name</replaceable></optional> <optional>, ...</optional>) </synopsis> - + <para> The <function>xmlforest</function> expression produces an XML forest (sequence) of elements using the given names and content. @@ -8432,18 +8432,18 @@ WHERE table_schema = 'pg_catalog'; <function>xmlelement</function>. </para> </sect3> - + <sect3> <title><literal>xmlpi</literal></title> - + <indexterm> <primary>xmlpi</primary> </indexterm> - + <synopsis> <function>xmlpi</function>(name <replaceable>target</replaceable> <optional>, <replaceable>content</replaceable></optional>) </synopsis> - + <para> The <function>xmlpi</function> expression creates an XML processing instruction. The content, if present, must not @@ -8461,18 +8461,18 @@ SELECT xmlpi(name php, 'echo "hello world";'); ]]></screen> </para> </sect3> - + <sect3> <title><literal>xmlroot</literal></title> - + <indexterm> <primary>xmlroot</primary> </indexterm> - + <synopsis> <function>xmlroot</function>(<replaceable>xml</replaceable>, version <replaceable>text</replaceable> | no value <optional>, standalone yes|no|no value</optional>) </synopsis> - + <para> The <function>xmlroot</function> expression alters the properties of the root node of an XML value. If a version is specified, @@ -8483,7 +8483,7 @@ SELECT xmlpi(name php, 'echo "hello world";'); <para> <screen><![CDATA[ -SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'), +SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'), version '1.0', standalone yes); xmlroot @@ -8636,17 +8636,19 @@ SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Tor <para> To process values of data type <type>xml</type>, PostgreSQL offers - the function <function>xpath</function>, which evaluates XPath 1.0 + the functions <function>xpath</function> and + <function>xpath_exists</function>, which evaluate XPath 1.0 expressions. </para> <synopsis> -<function>xpath</function>(<replaceable>xpath</replaceable>, <replaceable>xml</replaceable><optional>, <replaceable>nsarray</replaceable></optional>) +<function>xpath</function>(<replaceable>xpath</replaceable>, <replaceable>xml</replaceable> <optional>, <replaceable>nsarray</replaceable></optional>) </synopsis> <para> The function <function>xpath</function> evaluates the XPath - expression <replaceable>xpath</replaceable> against the XML value + expression <replaceable>xpath</replaceable> (a <type>text</> value) + against the XML value <replaceable>xml</replaceable>. It returns an array of XML values corresponding to the node set produced by the XPath expression. </para> @@ -8657,13 +8659,13 @@ SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Tor </para> <para> - The third argument of the function is an array of namespace - mappings. This array should be a two-dimensional array with the - length of the second axis being equal to 2 (i.e., it should be an + The optional third argument of the function is an array of namespace + mappings. This array should be a two-dimensional <type>text</> array with + the length of the second axis being equal to 2 (i.e., it should be an array of arrays, each of which consists of exactly 2 elements). The first element of each array entry is the namespace name (alias), the second the namespace URI. It is not required that aliases provided in - this array are the same that those being used in the XML document itself (in + this array be the same as those being used in the XML document itself (in other words, both in the XML document and in the <function>xpath</function> function context, aliases are <emphasis>local</>). </para> @@ -8671,7 +8673,7 @@ SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Tor <para> Example: <screen><![CDATA[ -SELECT xpath('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>', +SELECT xpath('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>', ARRAY[ARRAY['my', 'http://example.com']]); xpath @@ -8682,7 +8684,7 @@ SELECT xpath('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>', </para> <para> - How to deal with default (anonymous) namespaces: + To deal with default (anonymous) namespaces, do something like this: <screen><![CDATA[ SELECT xpath('//mydefns:b/text()', '<a xmlns="http://example.com"><b>test</b></a>', ARRAY[ARRAY['mydefns', 'http://example.com']]); @@ -8691,6 +8693,36 @@ SELECT xpath('//mydefns:b/text()', '<a xmlns="http://example.com"><b>test</b></a -------- {test} (1 row) +]]></screen> + </para> + + <indexterm> + <primary>xpath_exists</primary> + </indexterm> + +<synopsis> +<function>xpath_exists</function>(<replaceable>xpath</replaceable>, <replaceable>xml</replaceable> <optional>, <replaceable>nsarray</replaceable></optional>) +</synopsis> + + <para> + The function <function>xpath_exists</function> is a specialized form + of the <function>xpath</function> function. Instead of returning the + individual XML values that satisfy the XPath, this function returns a + boolean indicating whether the query was satisfied or not. This + function is equivalent to the standard <literal>XMLEXISTS</> predicate, + except that it also offers support for a namespace mapping argument. + </para> + + <para> + Example: +<screen><![CDATA[ +SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>', + ARRAY[ARRAY['my', 'http://example.com']]); + + xpath_exists +-------------- + t +(1 row) ]]></screen> </para> </sect2> @@ -8698,7 +8730,7 @@ SELECT xpath('//mydefns:b/text()', '<a xmlns="http://example.com"><b>test</b></a <sect2 id="functions-xml-mapping"> <title>Mapping Tables to XML</title> - <indexterm zone="functions-xml-mapping"> + <indexterm zone="functions-xml-mapping"> <primary>XML export</primary> </indexterm> diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 6587f4e4fc9..520668cf400 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.99 2010/08/05 04:21:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.100 2010/08/08 19:15:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3541,3 +3541,27 @@ Datum xmlexists(PG_FUNCTION_ARGS) return 0; #endif } + +/* + * Determines if the node specified by the supplied XPath exists + * in a given XML document, returning a boolean. Differs from + * xmlexists as it supports namespaces and is not defined in SQL/XML. + */ +Datum +xpath_exists(PG_FUNCTION_ARGS) +{ +#ifdef USE_LIBXML + text *xpath_expr_text = PG_GETARG_TEXT_P(0); + xmltype *data = PG_GETARG_XML_P(1); + ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2); + int res_nitems; + + xpath_internal(xpath_expr_text, data, namespaces, + &res_nitems, NULL); + + PG_RETURN_BOOL(res_nitems > 0); +#else + NO_XML_SUPPORT(); + return 0; +#endif +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 1decb54a99a..b4aeacb217e 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.592 2010/08/08 16:27:04 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.593 2010/08/08 19:15:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201008071 +#define CATALOG_VERSION_NO 201008081 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index bb8215c8fa8..812c65cb045 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.576 2010/08/08 16:27:04 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.577 2010/08/08 19:15:27 tgl Exp $ * * NOTES * The script catalog/genbki.pl reads this file and generates .bki @@ -4415,6 +4415,11 @@ DESCR("evaluate XPath expression"); DATA(insert OID = 2614 ( xmlexists PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "25 142" _null_ _null_ _null_ _null_ xmlexists _null_ _null_ _null_ )); DESCR("test XML value against XPath expression"); +DATA(insert OID = 3049 ( xpath_exists PGNSP PGUID 12 1 0 0 f f f t f i 3 0 16 "25 142 1009" _null_ _null_ _null_ _null_ xpath_exists _null_ _null_ _null_ )); +DESCR("test XML value against XPath expression, with namespace support"); +DATA(insert OID = 3050 ( xpath_exists PGNSP PGUID 14 1 0 0 f f f t f i 2 0 16 "25 142" _null_ _null_ _null_ _null_ "select pg_catalog.xpath_exists($1, $2, ''{}''::pg_catalog.text[])" _null_ _null_ _null_ )); +DESCR("test XML value against XPath expression"); + /* uuid */ DATA(insert OID = 2952 ( uuid_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2950 "2275" _null_ _null_ _null_ _null_ uuid_in _null_ _null_ _null_ )); DESCR("I/O"); diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h index 6815e266c7c..807bb08485f 100644 --- a/src/include/utils/xml.h +++ b/src/include/utils/xml.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.32 2010/08/05 04:21:54 petere Exp $ + * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.33 2010/08/08 19:15:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,6 +37,7 @@ extern Datum texttoxml(PG_FUNCTION_ARGS); extern Datum xmltotext(PG_FUNCTION_ARGS); extern Datum xmlvalidate(PG_FUNCTION_ARGS); extern Datum xpath(PG_FUNCTION_ARGS); +extern Datum xpath_exists(PG_FUNCTION_ARGS); extern Datum xmlexists(PG_FUNCTION_ARGS); extern Datum table_to_xml(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out index 439fef4877b..435331dcc37 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -502,7 +502,7 @@ SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>'); {<b>two</b>,<b>etc</b>} (1 row) --- Test xmlexists evaluation +-- 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 ----------- @@ -515,6 +515,18 @@ SELECT xmlexists('//town[text() = ''Cwmbran'']' PASSING BY REF '<towns><town>Bid 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 +-------------- + f +(1 row) + +SELECT xpath_exists('//town[text() = ''Cwmbran'']','<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>'::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); @@ -543,6 +555,42 @@ SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers/name[text() = ''Molso 1 (1 row) +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beer',data); + count +------- + 0 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers',data); + count +------- + 2 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers/name[text() = ''Molson'']',data); + count +------- + 1 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beer',data,ARRAY[ARRAY['myns','http://myns.com']]); + count +------- + 0 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers',data,ARRAY[ARRAY['myns','http://myns.com']]); + count +------- + 2 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers/myns:name[text() = ''Molson'']',data,ARRAY[ARRAY['myns','http://myns.com']]); + count +------- + 1 +(1 row) + CREATE TABLE query ( expr TEXT ); INSERT INTO query VALUES ('/menu/beers/cost[text() = ''lots'']'); SELECT COUNT(id) FROM xmltest, query WHERE xmlexists(expr PASSING BY REF data); diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out index d15e50a1b9b..2ce543aeaa0 100644 --- a/src/test/regress/expected/xml_1.out +++ b/src/test/regress/expected/xml_1.out @@ -456,7 +456,7 @@ LINE 1: SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>'... ^ DETAIL: This functionality requires the server to be built with libxml support. HINT: You need to rebuild PostgreSQL using --with-libxml. --- Test xmlexists evaluation +-- 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 LINE 1: ...sts('//town[text() = ''Toronto'']' PASSING BY REF '<towns><t... @@ -469,6 +469,18 @@ LINE 1: ...sts('//town[text() = ''Cwmbran'']' PASSING BY REF '<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('//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('//town[text() = ''Cwmbran'']','<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() = ''Cwmbran'']','<towns><t... + ^ +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... @@ -517,6 +529,42 @@ SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers/name[text() = ''Molso 0 (1 row) +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beer',data); + count +------- + 0 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers',data); + count +------- + 0 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers/name[text() = ''Molson'']',data); + count +------- + 0 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beer',data,ARRAY[ARRAY['myns','http://myns.com']]); + count +------- + 0 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers',data,ARRAY[ARRAY['myns','http://myns.com']]); + count +------- + 0 +(1 row) + +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers/myns:name[text() = ''Molson'']',data,ARRAY[ARRAY['myns','http://myns.com']]); + count +------- + 0 +(1 row) + CREATE TABLE query ( expr TEXT ); INSERT INTO query VALUES ('/menu/beers/cost[text() = ''lots'']'); SELECT COUNT(id) FROM xmltest, query WHERE xmlexists(expr PASSING BY REF data); diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql index 4c88befc403..0e8c0fb2273 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -164,21 +164,29 @@ SELECT xpath('//text()', '<local:data xmlns:local="http://127.0.0.1"><local:piec SELECT xpath('//loc:piece/@id', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>', ARRAY[ARRAY['loc', 'http://127.0.0.1']]); SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>'); --- Test xmlexists evaluation +-- 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 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); 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); INSERT INTO xmltest VALUES (7, '<myns:menu xmlns:myns="http://myns.com"><myns:beers><myns:name>Molson</myns:name><myns:cost>free</myns:cost><myns:name>Carling</myns:name><myns:cost>lots</myns:cost></myns:beers></myns:menu>'::xml); - SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beer' PASSING data); SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beer' PASSING BY REF data BY REF); SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers' PASSING BY REF data); SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers/name[text() = ''Molson'']' PASSING BY REF data); +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beer',data); +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers',data); +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers/name[text() = ''Molson'']',data); +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beer',data,ARRAY[ARRAY['myns','http://myns.com']]); +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers',data,ARRAY[ARRAY['myns','http://myns.com']]); +SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers/myns:name[text() = ''Molson'']',data,ARRAY[ARRAY['myns','http://myns.com']]); + CREATE TABLE query ( expr TEXT ); INSERT INTO query VALUES ('/menu/beers/cost[text() = ''lots'']'); SELECT COUNT(id) FROM xmltest, query WHERE xmlexists(expr PASSING BY REF data); -- GitLab