Add xmlexists function
authorPeter Eisentraut
Thu, 5 Aug 2010 04:21:54 +0000 (04:21 +0000)
committerPeter Eisentraut
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 @@
-
+
 
  
   Functions and Operators
@@ -8554,10 +8554,19 @@ SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
 ]]>
     
    
+   
 
-   3>
+   2>
     XML Predicates
 
+    
+     The expressions described in this section check properties
+     of xml values.
+    
+
+   
+    IS DOCUMENT
+
     
      IS DOCUMENT
     
@@ -8574,6 +8583,48 @@ SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
      between documents and content fragments.
     
    
+
+   
+    XMLEXISTS
+
+    
+     XMLEXISTS
+    
+
+
+XMLEXISTS(text PASSING BY REF xml BY REF)
+
+
+    
+     The function xmlexists 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.)
+    
+
+    
+     Example:
+     
+SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'TorontoOttawa');
+
+ xmlexists
+------------
+ t
+(1 row)
+]]>
+    
+
+    
+     The BY REF clauses have no effect in
+     PostgreSQL, but are allowed for SQL conformance and compatibility
+     with other implementations.  Per SQL standard, the
+     first BY REF is required, the second is
+     optional.  Also note that the SQL standard specifies
+     the xmlexists construct to take an XQuery
+     expression as first argument, but PostgreSQL currently only
+     supports XPath, which is a subset of XQuery.
+    
+   
   
 
   
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  xml_attribute_el
 %type    xml_attribute_list xml_attributes
 %type    xml_root_version opt_xml_root_standalone
+%type    xmlexists_argument
 %type    document_or_content
 %type  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', 'one two three etc');
  {two,etc}
 (1 row)
 
+-- Test xmlexists evaluation
+SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol');
+ xmlexists 
+-----------
+ f
+(1 row)
+
+SELECT xmlexists('//town[text() = ''Cwmbran'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol');
+ xmlexists 
+-----------
+ t
+(1 row)
+
+INSERT INTO xmltest VALUES (4, 'BudvarfreeCarlinglots'::xml);
+INSERT INTO xmltest VALUES (5, 'MolsonfreeCarlinglots'::xml);
+INSERT INTO xmltest VALUES (6, 'BudvarfreeCarlinglots'::xml);
+INSERT INTO xmltest VALUES (7, 'MolsonfreeCarlinglots'::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', 'one two three etc'...
                             ^
 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 'Bidford-on-AvonCwmbranBristol');
+ERROR:  unsupported XML feature
+LINE 1: ...sts('//town[text() = ''Toronto'']' 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 xmlexists('//town[text() = ''Cwmbran'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol');
+ERROR:  unsupported XML feature
+LINE 1: ...sts('//town[text() = ''Cwmbran'']' PASSING BY REF '
+                                                             ^
+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
+                                       ^
+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, 'MolsonfreeCarlinglots'::xml);
+ERROR:  unsupported XML feature
+LINE 1: INSERT INTO xmltest VALUES (5, 'Molson
+                                       ^
+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, 'BudvarfreeCarlinglots'::xml);
+ERROR:  unsupported XML feature
+LINE 1: INSERT INTO xmltest VALUES (6, '
+                                       ^
+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, 'MolsonfreeCarlinglots'::xml);
+ERROR:  unsupported XML feature
+LINE 1: INSERT INTO xmltest VALUES (7, '
+                                       ^
+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('', '');
 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');
+
+-- Test xmlexists evaluation
+SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol');
+SELECT xmlexists('//town[text() = ''Cwmbran'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol');
+
+INSERT INTO xmltest VALUES (4, 'BudvarfreeCarlinglots'::xml);
+INSERT INTO xmltest VALUES (5, 'MolsonfreeCarlinglots'::xml);
+INSERT INTO xmltest VALUES (6, 'BudvarfreeCarlinglots'::xml);
+INSERT INTO xmltest VALUES (7, 'MolsonfreeCarlinglots'::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);