Add xmlexists function

by Mike Fowler, reviewed by Peter Eisentraut
This commit is contained in:
Peter Eisentraut 2010-08-05 04:21:54 +00:00
parent 26e47efb66
commit 641459f269
10 changed files with 312 additions and 31 deletions

View 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,9 +8554,18 @@ SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
]]></screen>
</para>
</sect3>
</sect2>
<sect2>
<title>XML Predicates</title>
<para>
The expressions described in this section check properties
of <type>xml</type> values.
</para>
<sect3>
<title>XML Predicates</title>
<title>IS DOCUMENT</title>
<indexterm>
<primary>IS DOCUMENT</primary>
@ -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">

View 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

View 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
}

View 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

View 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");

View 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)

View 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);

View 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)

View 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)

View 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);