]> granicus.if.org Git - postgresql/commitdiff
Add an xpath_exists() function. This is equivalent to XMLEXISTS except that
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 8 Aug 2010 19:15:27 +0000 (19:15 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 8 Aug 2010 19:15:27 +0000 (19:15 +0000)
it offers support for namespace mapping.

Mike Fowler, reviewed by David Fetter

doc/src/sgml/func.sgml
src/backend/utils/adt/xml.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/xml.h
src/test/regress/expected/xml.out
src/test/regress/expected/xml_1.out
src/test/regress/sql/xml.sql

index b981ca7f9209e39c989ea05c48dfcbb17624813b..57b6f7aaa2ab9205e78c014284be83868844f408 100644 (file)
@@ -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>
 
index 6587f4e4fc9bc4f841d066b5a67a8d73c6379917..520668cf400e201423e897021d0b91fb6097966f 100644 (file)
@@ -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
+}
index 1decb54a99af6c77ad2ae653c1a55f8f9849d0ef..b4aeacb217e1c19a1a76945dcae358ab62c993b9 100644 (file)
@@ -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
index bb8215c8fa8ffaec184dd2d8c6f67b7b022bb461..812c65cb0455a80488f887f53d6d70092001d81c 100644 (file)
@@ -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");
index 6815e266c7ce7868897ed1ee1740d72b68749f27..807bb08485f6548e1a895bc73811691c18fd4e1d 100644 (file)
@@ -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);
index 439fef4877b73be6b9d8ce441e81e177fab3fb49..435331dcc37cf050ce6e29654936446774b46d6c 100644 (file)
@@ -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);
index d15e50a1b9b90590d75d67260d22bff19a71c990..2ce543aeaa04734e6c272d3dd690afbb24127078 100644 (file)
@@ -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);
index 4c88befc403a49af796d1dbeee990ab49b989a4c..0e8c0fb2273b904a93f7e9699120b9067a6bdb41 100644 (file)
@@ -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);