]> granicus.if.org Git - postgresql/commitdiff
Add xmlexists function
authorPeter Eisentraut <peter_e@gmx.net>
Thu, 5 Aug 2010 04:21:54 +0000 (04:21 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Thu, 5 Aug 2010 04:21:54 +0000 (04:21 +0000)
by Mike Fowler, reviewed by Peter Eisentraut

doc/src/sgml/func.sgml
src/backend/parser/gram.y
src/backend/utils/adt/xml.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/parser/kwlist.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 dbe0d460698151690b8d66c21930ec64c320573a..07ff132cbb5353e93d627044989e255c8e83cfc7 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.522 2010/07/29 19:34:40 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.523 2010/08/05 04:21:53 petere Exp $ -->
 
  <chapter id="functions">
   <title>Functions and Operators</title>
@@ -8554,10 +8554,19 @@ SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
 ]]></screen>
     </para>
    </sect3>
+   </sect2>
 
-   <sect3>
+   <sect2>
     <title>XML Predicates</title>
 
+    <para>
+     The expressions described in this section check properties
+     of <type>xml</type> values.
+    </para>
+
+   <sect3>
+    <title>IS DOCUMENT</title>
+
     <indexterm>
      <primary>IS DOCUMENT</primary>
     </indexterm>
@@ -8574,6 +8583,48 @@ SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
      between documents and content fragments.
     </para>
    </sect3>
+
+   <sect3>
+    <title>XMLEXISTS</title>
+
+    <indexterm>
+     <primary>XMLEXISTS</primary>
+    </indexterm>
+
+<synopsis>
+<function>XMLEXISTS</function>(<replaceable>text</replaceable> PASSING <optional>BY REF</optional> <replaceable>xml</replaceable> <optional>BY REF</optional>)
+</synopsis>
+
+    <para>
+     The function <function>xmlexists</function> returns true if the
+     XPath expression in the first argument returns any nodes, and
+     false otherwise.  (If either argument is null, the result is
+     null.)
+    </para>
+
+    <para>
+     Example:
+     <screen><![CDATA[
+SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Toronto</town><town>Ottawa</town></towns>');
+
+ xmlexists
+------------
+ t
+(1 row)
+]]></screen>
+    </para>
+
+    <para>
+     The <literal>BY REF</literal> clauses have no effect in
+     PostgreSQL, but are allowed for SQL conformance and compatibility
+     with other implementations.  Per SQL standard, the
+     first <literal>BY REF</literal> is required, the second is
+     optional.  Also note that the SQL standard specifies
+     the <function>xmlexists</function> construct to take an XQuery
+     expression as first argument, but PostgreSQL currently only
+     supports XPath, which is a subset of XQuery.
+    </para>
+   </sect3>
   </sect2>
 
   <sect2 id="functions-xml-processing">
index 07ee2a834966fe7fc0923a53cd24cb5994d04d44..d9aebb0d7273216becb0d2715c8d4bcac541b00b 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.714 2010/07/25 23:21:21 rhaas Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.715 2010/08/05 04:21:53 petere Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -425,6 +425,7 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <target> xml_attribute_el
 %type <list>   xml_attribute_list xml_attributes
 %type <node>   xml_root_version opt_xml_root_standalone
+%type <node>   xmlexists_argument
 %type <ival>   document_or_content
 %type <boolean> xml_whitespace_option
 
@@ -511,13 +512,13 @@ static TypeName *TableFuncTypeName(List *columns);
        OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
        ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
 
-       PARSER PARTIAL PARTITION PASSWORD PLACING PLANS POSITION
+       PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POSITION
        PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
        PRIOR PRIVILEGES PROCEDURAL PROCEDURE
 
        QUOTE
 
-       RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX
+       RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX
        RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
        RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
 
@@ -539,7 +540,7 @@ static TypeName *TableFuncTypeName(List *columns);
 
        WHEN WHERE WHITESPACE_P WINDOW WITH WITHOUT WORK WRAPPER WRITE
 
-       XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE
+       XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE
        XMLPI XMLROOT XMLSERIALIZE
 
        YEAR_P YES_P
@@ -9839,6 +9840,21 @@ func_expr:       func_name '(' ')' over_clause
                                {
                                        $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1);
                                }
+                       | XMLEXISTS '(' c_expr xmlexists_argument ')'
+                               {
+                                       /* xmlexists(A PASSING [BY REF] B [BY REF]) is
+                                        * converted to xmlexists(A, B)*/
+                                       FuncCall *n = makeNode(FuncCall);
+                                       n->funcname = SystemFuncName("xmlexists");
+                                       n->args = list_make2($3, $4);
+                                       n->agg_order = NIL;
+                                       n->agg_star = FALSE;
+                                       n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
+                                       n->over = NULL;
+                                       n->location = @1;
+                                       $$ = (Node *)n;
+                               }
                        | XMLFOREST '(' xml_attribute_list ')'
                                {
                                        $$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1);
@@ -9929,6 +9945,27 @@ xml_whitespace_option: PRESERVE WHITESPACE_P             { $$ = TRUE; }
                        | /*EMPTY*/                                                             { $$ = FALSE; }
                ;
 
+/* We allow several variants for SQL and other compatibility. */
+xmlexists_argument:
+                       PASSING c_expr
+                               {
+                                       $$ = $2;
+                               }
+                       | PASSING c_expr BY REF
+                               {
+                                       $$ = $2;
+                               }
+                       | PASSING BY REF c_expr
+                               {
+                                       $$ = $4;
+                               }
+                       | PASSING BY REF c_expr BY REF
+                               {
+                                       $$ = $4;
+                               }
+               ;
+
+
 /*
  * Window Definitions
  */
@@ -10999,6 +11036,7 @@ unreserved_keyword:
                        | PARSER
                        | PARTIAL
                        | PARTITION
+                       | PASSING
                        | PASSWORD
                        | PLANS
                        | PRECEDING
@@ -11015,6 +11053,7 @@ unreserved_keyword:
                        | REASSIGN
                        | RECHECK
                        | RECURSIVE
+                       | REF
                        | REINDEX
                        | RELATIVE_P
                        | RELEASE
@@ -11148,6 +11187,7 @@ col_name_keyword:
                        | XMLATTRIBUTES
                        | XMLCONCAT
                        | XMLELEMENT
+                       | XMLEXISTS
                        | XMLFOREST
                        | XMLPARSE
                        | XMLPI
index eaf5b4d550274a4692d6ef34ecb877cf9f631e5d..6587f4e4fc9bc4f841d066b5a67a8d73c6379917 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.98 2010/07/06 19:18:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.99 2010/08/05 04:21:54 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3295,24 +3295,20 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
 
 
 /*
- * Evaluate XPath expression and return array of XML values.
+ * Common code for xpath() and xmlexists()
  *
- * As we have no support of XQuery sequences yet, this function seems
- * to be the most useful one (array of XML functions plays a role of
- * some kind of substitution for XQuery sequences).
+ * Evaluate XPath expression and return number of nodes in res_items
+ * and array of XML values in astate.
  *
  * 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.
  */
-Datum
-xpath(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);
-       ArrayBuildState *astate = NULL;
+static void
+xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
+                          int *res_nitems, ArrayBuildState **astate)
+{
        xmlParserCtxtPtr ctxt = NULL;
        xmlDocPtr       doc = NULL;
        xmlXPathContextPtr xpathctx = NULL;
@@ -3324,7 +3320,6 @@ xpath(PG_FUNCTION_ARGS)
        xmlChar    *string;
        xmlChar    *xpath_expr;
        int                     i;
-       int                     res_nitems;
        int                     ndim;
        Datum      *ns_names_uris;
        bool       *ns_names_uris_nulls;
@@ -3339,7 +3334,7 @@ xpath(PG_FUNCTION_ARGS)
         * ARRAY[ARRAY['myns', 'http://example.com'], ARRAY['myns2',
         * 'http://example2.com']].
         */
-       ndim = ARR_NDIM(namespaces);
+       ndim = namespaces ? ARR_NDIM(namespaces) : 0;
        if (ndim != 0)
        {
                int                *dims;
@@ -3439,6 +3434,13 @@ xpath(PG_FUNCTION_ARGS)
                        xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
                                                "invalid XPath expression");
 
+               /*
+                * Version 2.6.27 introduces a function named
+                * xmlXPathCompiledEvalToBoolean, which would be enough for
+                * xmlexists, but we can derive the existence by whether any
+                * nodes are returned, thereby preventing a library version
+                * upgrade and keeping the code the same.
+                */
                xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
                if (xpathobj == NULL)   /* TODO: reason? */
                        xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
@@ -3446,21 +3448,22 @@ xpath(PG_FUNCTION_ARGS)
 
                /* return empty array in cases when nothing is found */
                if (xpathobj->nodesetval == NULL)
-                       res_nitems = 0;
+                       *res_nitems = 0;
                else
-                       res_nitems = xpathobj->nodesetval->nodeNr;
+                       *res_nitems = xpathobj->nodesetval->nodeNr;
 
-               if (res_nitems)
+               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);
+                               *astate = accumArrayResult(*astate, elem,
+                                                                                  elemisnull, XMLOID,
+                                                                                  CurrentMemoryContext);
                        }
                }
        }
@@ -3485,6 +3488,28 @@ xpath(PG_FUNCTION_ARGS)
        xmlXPathFreeContext(xpathctx);
        xmlFreeDoc(doc);
        xmlFreeParserCtxt(ctxt);
+}
+#endif /* USE_LIBXML */
+
+/*
+ * Evaluate XPath expression and return array of XML values.
+ *
+ * As we have no support of XQuery sequences yet, this function seems
+ * to be the most useful one (array of XML functions plays a role of
+ * some kind of substitution for XQuery sequences).
+ */
+Datum
+xpath(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;
+       ArrayBuildState *astate;
+
+       xpath_internal(xpath_expr_text, data, namespaces,
+                                  &res_nitems, &astate);
 
        if (res_nitems == 0)
                PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID));
@@ -3495,3 +3520,24 @@ xpath(PG_FUNCTION_ARGS)
        return 0;
 #endif
 }
+
+/*
+ * Determines if the node specified by the supplied XPath exists
+ * in a given XML document, returning a boolean.
+ */
+Datum xmlexists(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+       text       *xpath_expr_text = PG_GETARG_TEXT_P(0);
+       xmltype    *data = PG_GETARG_XML_P(1);
+       int                     res_nitems;
+
+       xpath_internal(xpath_expr_text, data, NULL,
+                                  &res_nitems, NULL);
+
+       PG_RETURN_BOOL(res_nitems > 0);
+#else
+       NO_XML_SUPPORT();
+       return 0;
+#endif
+}
index ac52eb18bc7113c4239ab71aa4762199fb650cbb..a1a223b227d8fcb06f3ee487bfef32d2771eca54 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.588 2010/07/16 02:15:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.589 2010/08/05 04:21:54 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201007151
+#define CATALOG_VERSION_NO     201008051
 
 #endif
index a505770c4fe170ecd64a2f3aea513d3816e83479..8b9e1db5815d5a120c338fa93052c0a75e8111d3 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.573 2010/07/29 20:09:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.574 2010/08/05 04:21:54 petere Exp $
  *
  * NOTES
  *       The script catalog/genbki.pl reads this file and generates .bki
@@ -4391,6 +4391,9 @@ DESCR("evaluate XPath expression, with namespaces support");
 DATA(insert OID = 2932 (  xpath                 PGNSP PGUID 14 1 0 0 f f f t f i 2 0 143 "25 142" _null_ _null_ _null_ _null_ "select pg_catalog.xpath($1, $2, ''{}''::pg_catalog.text[])" _null_ _null_ _null_ ));
 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");
+
 /* 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 5065bd609e9a36069a9d40e75f8727526799ef52..271c5ca7b613bc19bfce69a1d534ed236cda1c21 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.12 2010/02/12 17:33:21 tgl Exp $
+ *       $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.13 2010/08/05 04:21:54 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -280,6 +280,7 @@ PG_KEYWORD("owner", OWNER, UNRESERVED_KEYWORD)
 PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD)
 PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD)
 PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD)
+PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD)
 PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD)
 PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD)
 PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD)
@@ -301,6 +302,7 @@ PG_KEYWORD("real", REAL, COL_NAME_KEYWORD)
 PG_KEYWORD("reassign", REASSIGN, UNRESERVED_KEYWORD)
 PG_KEYWORD("recheck", RECHECK, UNRESERVED_KEYWORD)
 PG_KEYWORD("recursive", RECURSIVE, UNRESERVED_KEYWORD)
+PG_KEYWORD("ref", REF, UNRESERVED_KEYWORD)
 PG_KEYWORD("references", REFERENCES, RESERVED_KEYWORD)
 PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD)
 PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD)
@@ -413,6 +415,7 @@ PG_KEYWORD("xml", XML_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("xmlattributes", XMLATTRIBUTES, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlconcat", XMLCONCAT, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlelement", XMLELEMENT, COL_NAME_KEYWORD)
+PG_KEYWORD("xmlexists", XMLEXISTS, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlforest", XMLFOREST, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlparse", XMLPARSE, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlpi", XMLPI, COL_NAME_KEYWORD)
index 7543cbb6ee481b57ab7511e65471e9bf94a4bd94..6815e266c7ce7868897ed1ee1740d72b68749f27 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.31 2010/03/03 17:29:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.32 2010/08/05 04:21:54 petere 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 xmlexists(PG_FUNCTION_ARGS);
 
 extern Datum table_to_xml(PG_FUNCTION_ARGS);
 extern Datum query_to_xml(PG_FUNCTION_ARGS);
index ecca5896a70c2afe483e936a3b6eb2eb334881b4..439fef4877b73be6b9d8ce441e81e177fab3fb49 100644 (file)
@@ -502,3 +502,52 @@ SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
  {<b>two</b>,<b>etc</b>}
 (1 row)
 
+-- Test xmlexists evaluation
+SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF '<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>');
+ xmlexists 
+-----------
+ f
+(1 row)
+
+SELECT xmlexists('//town[text() = ''Cwmbran'']' PASSING BY REF '<towns><town>Bidford-on-Avon</town><town>Cwmbran</town><town>Bristol</town></towns>');
+ xmlexists 
+-----------
+ 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);
+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);
+ count 
+-------
+     0
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beer' PASSING BY REF data BY REF);
+ count 
+-------
+     0
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers' PASSING BY REF data);
+ count 
+-------
+     2
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers/name[text() = ''Molson'']' PASSING BY REF data);
+ 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);
+ count 
+-------
+     2
+(1 row)
+
index d542b0689a9dbc386bfb8c57237d923224488383..d15e50a1b9b90590d75d67260d22bff19a71c990 100644 (file)
@@ -456,3 +456,72 @@ 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
+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...
+                                                             ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT xmlexists('//town[text() = ''Cwmbran'']' 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() = ''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.
+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...
+                                       ^
+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 (5, '<menu><beers><name>Molson</name><cost>free</cost><name>Carling</name><cost>lots</cost></beers></menu>'::xml);
+ERROR:  unsupported XML feature
+LINE 1: INSERT INTO xmltest VALUES (5, '<menu><beers><name>Molson</n...
+                                       ^
+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 (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);
+ERROR:  unsupported XML feature
+LINE 1: INSERT INTO xmltest VALUES (6, '<myns:menu xmlns:myns="http:...
+                                       ^
+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 (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);
+ERROR:  unsupported XML feature
+LINE 1: INSERT INTO xmltest VALUES (7, '<myns:menu xmlns:myns="http:...
+                                       ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beer' PASSING data);
+ count 
+-------
+     0
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beer' PASSING BY REF data BY REF);
+ count 
+-------
+     0
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers' PASSING BY REF data);
+ count 
+-------
+     0
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers/name[text() = ''Molson'']' PASSING BY REF data);
+ 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);
+ count 
+-------
+     0
+(1 row)
+
index 086eedd27004edde15ab540280b3469dde846692..4c88befc403a49af796d1dbeee990ab49b989a4c 100644 (file)
@@ -163,3 +163,22 @@ SELECT xpath('', '<!-- error -->');
 SELECT xpath('//text()', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>');
 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
+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>');
+
+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);
+
+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);