From 4dfc4578548ea8f1bf69da5424d1e76a0b110a75 Mon Sep 17 00:00:00 2001
From: Tom Lane
Date: Sun, 8 Aug 2010 19:15:27 +0000
Subject: Add an xpath_exists() function. This is equivalent to XMLEXISTS
except that it offers support for namespace mapping.
Mike Fowler, reviewed by David Fetter
---
src/backend/utils/adt/xml.c | 26 ++++++++++++++++++-
src/include/catalog/catversion.h | 4 +--
src/include/catalog/pg_proc.h | 7 +++++-
src/include/utils/xml.h | 3 ++-
src/test/regress/expected/xml.out | 50 ++++++++++++++++++++++++++++++++++++-
src/test/regress/expected/xml_1.out | 50 ++++++++++++++++++++++++++++++++++++-
src/test/regress/sql/xml.sql | 12 +++++++--
7 files changed, 143 insertions(+), 9 deletions(-)
(limited to 'src')
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 6587f4e4fc9..520668cf400 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.99 2010/08/05 04:21:54 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.100 2010/08/08 19:15:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -3541,3 +3541,27 @@ Datum xmlexists(PG_FUNCTION_ARGS)
return 0;
#endif
}
+
+/*
+ * Determines if the node specified by the supplied XPath exists
+ * in a given XML document, returning a boolean. Differs from
+ * xmlexists as it supports namespaces and is not defined in SQL/XML.
+ */
+Datum
+xpath_exists(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+ text *xpath_expr_text = PG_GETARG_TEXT_P(0);
+ xmltype *data = PG_GETARG_XML_P(1);
+ ArrayType *namespaces = PG_GETARG_ARRAYTYPE_P(2);
+ int res_nitems;
+
+ xpath_internal(xpath_expr_text, data, namespaces,
+ &res_nitems, NULL);
+
+ PG_RETURN_BOOL(res_nitems > 0);
+#else
+ NO_XML_SUPPORT();
+ return 0;
+#endif
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 1decb54a99a..b4aeacb217e 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.592 2010/08/08 16:27:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.593 2010/08/08 19:15:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201008071
+#define CATALOG_VERSION_NO 201008081
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index bb8215c8fa8..812c65cb045 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.576 2010/08/08 16:27:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.577 2010/08/08 19:15:27 tgl Exp $
*
* NOTES
* The script catalog/genbki.pl reads this file and generates .bki
@@ -4415,6 +4415,11 @@ DESCR("evaluate XPath expression");
DATA(insert OID = 2614 ( xmlexists PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "25 142" _null_ _null_ _null_ _null_ xmlexists _null_ _null_ _null_ ));
DESCR("test XML value against XPath expression");
+DATA(insert OID = 3049 ( xpath_exists PGNSP PGUID 12 1 0 0 f f f t f i 3 0 16 "25 142 1009" _null_ _null_ _null_ _null_ xpath_exists _null_ _null_ _null_ ));
+DESCR("test XML value against XPath expression, with namespace support");
+DATA(insert OID = 3050 ( xpath_exists PGNSP PGUID 14 1 0 0 f f f t f i 2 0 16 "25 142" _null_ _null_ _null_ _null_ "select pg_catalog.xpath_exists($1, $2, ''{}''::pg_catalog.text[])" _null_ _null_ _null_ ));
+DESCR("test XML value against XPath expression");
+
/* uuid */
DATA(insert OID = 2952 ( uuid_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2950 "2275" _null_ _null_ _null_ _null_ uuid_in _null_ _null_ _null_ ));
DESCR("I/O");
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 6815e266c7c..807bb08485f 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.32 2010/08/05 04:21:54 petere Exp $
+ * $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.33 2010/08/08 19:15:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -37,6 +37,7 @@ extern Datum texttoxml(PG_FUNCTION_ARGS);
extern Datum xmltotext(PG_FUNCTION_ARGS);
extern Datum xmlvalidate(PG_FUNCTION_ARGS);
extern Datum xpath(PG_FUNCTION_ARGS);
+extern Datum xpath_exists(PG_FUNCTION_ARGS);
extern Datum xmlexists(PG_FUNCTION_ARGS);
extern Datum table_to_xml(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index 439fef4877b..435331dcc37 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -502,7 +502,7 @@ SELECT xpath('//b', 'one two three etc');
{two,etc}
(1 row)
--- Test xmlexists evaluation
+-- Test xmlexists and xpath_exists
SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol');
xmlexists
-----------
@@ -515,6 +515,18 @@ SELECT xmlexists('//town[text() = ''Cwmbran'']' PASSING BY REF 'Bid
t
(1 row)
+SELECT xpath_exists('//town[text() = ''Toronto'']','Bidford-on-AvonCwmbranBristol'::xml);
+ xpath_exists
+--------------
+ f
+(1 row)
+
+SELECT xpath_exists('//town[text() = ''Cwmbran'']','Bidford-on-AvonCwmbranBristol'::xml);
+ xpath_exists
+--------------
+ t
+(1 row)
+
INSERT INTO xmltest VALUES (4, ''::xml);
INSERT INTO xmltest VALUES (5, ''::xml);
INSERT INTO xmltest VALUES (6, 'BudvarfreeCarlinglots'::xml);
@@ -543,6 +555,42 @@ SELECT COUNT(id) FROM xmltest WHERE xmlexists('/menu/beers/name[text() = ''Molso
1
(1 row)
+SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beer',data);
+ count
+-------
+ 0
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers',data);
+ count
+-------
+ 2
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/menu/beers/name[text() = ''Molson'']',data);
+ count
+-------
+ 1
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beer',data,ARRAY[ARRAY['myns','http://myns.com']]);
+ count
+-------
+ 0
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers',data,ARRAY[ARRAY['myns','http://myns.com']]);
+ count
+-------
+ 2
+(1 row)
+
+SELECT COUNT(id) FROM xmltest WHERE xpath_exists('/myns:menu/myns:beers/myns:name[text() = ''Molson'']',data,ARRAY[ARRAY['myns','http://myns.com']]);
+ count
+-------
+ 1
+(1 row)
+
CREATE TABLE query ( expr TEXT );
INSERT INTO query VALUES ('/menu/beers/cost[text() = ''lots'']');
SELECT COUNT(id) FROM xmltest, query WHERE xmlexists(expr PASSING BY REF data);
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index d15e50a1b9b..2ce543aeaa0 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -456,7 +456,7 @@ LINE 1: SELECT xpath('//b', '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
+-- Test xmlexists and xpath_exists
SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol');
ERROR: unsupported XML feature
LINE 1: ...sts('//town[text() = ''Toronto'']' PASSING BY REF 'Bidford-on-AvonCwmbranBristol'::xml);
+ERROR: unsupported XML feature
+LINE 1: ...ELECT xpath_exists('//town[text() = ''Toronto'']','Bidford-on-AvonCwmbranBristol'::xml);
+ERROR: unsupported XML feature
+LINE 1: ...ELECT xpath_exists('//town[text() = ''Cwmbran'']','BudvarfreeCarlinglots'::xml);
ERROR: unsupported XML feature
LINE 1: INSERT INTO xmltest VALUES (4, '