XPath fixes:

- Function renamed to "xpath".
 - Function is now strict, per discussion.
 - Return empty array in case when XPath expression detects nothing
   (previously, NULL was returned in such case), per discussion.
 - (bugfix) Work with fragments with prologue: select xpath('/a',
   '<?xml version="1.0"?><a /><b />'); // now XML datum is always wrapped
   with dummy <x>...</x>, XML prologue simply goes away (if any).
 - Some cleanup.

Nikolay Samokhvalov

Some code cleanup and documentation work by myself.
This commit is contained in:
Peter Eisentraut 2007-05-21 17:10:29 +00:00
parent 0c644d2c3d
commit 3963574d13
9 changed files with 238 additions and 205 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.200 2007/05/08 17:02:59 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.201 2007/05/21 17:10:28 petere Exp $ -->
<chapter id="datatype">
<title id="datatype-title">Data Types</title>
@ -3213,7 +3213,7 @@ SELECT * FROM test;
<sect1 id="datatype-uuid">
<title><acronym>UUID</acronym> Type</title>
<indexterm zone="datatype-xml">
<indexterm zone="datatype-uuid">
<primary>UUID</primary>
</indexterm>
@ -3289,6 +3289,8 @@ a0eebc999c0b4ef8bb6d6bb9bd380a11
value is a full document or only a content fragment.
</para>
<sect2>
<title>Creating XML Values</title>
<para>
To produce a value of type <type>xml</type> from character data,
use the function
@ -3299,7 +3301,7 @@ XMLPARSE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable>)
Examples:
<programlisting><![CDATA[
XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Manual</title><chapter>...</chapter><book>')
XMLPARSE (CONTENT 'abc<foo>bar</bar><bar>foo</foo>')
XMLPARSE (CONTENT 'abc<foo>bar</foo><bar>foo</bar>')
]]></programlisting>
While this is the only way to convert character strings into XML
values according to the SQL standard, the PostgreSQL-specific
@ -3351,7 +3353,10 @@ SET xmloption TO { DOCUMENT | CONTENT };
The default is <literal>CONTENT</literal>, so all forms of XML
data are allowed.
</para>
</sect2>
<sect2>
<title>Encoding Handling</title>
<para>
Care must be taken when dealing with multiple character encodings
on the client, server, and in the XML data passed through them.
@ -3398,6 +3403,41 @@ SET xmloption TO { DOCUMENT | CONTENT };
processed in UTF-8, computations will be most efficient if the
server encoding is also UTF-8.
</para>
</sect2>
<sect2>
<title>Accessing XML Values</title>
<para>
The <type>xml</type> data type is unusual in that it does not
provide any comparison operators. This is because there is no
well-defined and universally useful comparison algorithm for XML
data. One consequence of this is that you cannot retrieve rows by
comparing an <type>xml</type> column against a search value. XML
values should therefore typically be accompanied by a separate key
field such as an ID. An alternative solution for comparing XML
values is to convert them to character strings first, but note
that character string comparison has little to do with a useful
XML comparison method.
</para>
<para>
Since there are no comparison operators for the <type>xml</type>
data type, it is not possible to create an index directly on a
column of this type. If speedy searches in XML data are desired,
possible workarounds would be casting the expression to a
character string type and indexing that, or indexing an XPath
expression. The actual query would of course have to be adjusted
to search by the indexed expression.
</para>
<para>
The full-text search module Tsearch2 could also be used to speed
up full-document searches in XML data. The necessary
preprocessing support is, however, not available in the PostgreSQL
distribution in this release.
</para>
</sect2>
</sect1>
&array;

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.379 2007/05/07 07:53:26 petere Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.380 2007/05/21 17:10:28 petere Exp $ -->
<chapter id="functions">
<title>Functions and Operators</title>
@ -7512,7 +7512,7 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple
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>xml</type> functions requires the installation to have been built
functions requires the installation to have been built
with <command>configure --with-libxml</>.
</para>
@ -7848,6 +7848,51 @@ SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
</sect3>
</sect2>
<sect2 id="functions-xml-processing">
<title>Processing XML</title>
<indexterm>
<primary>XPath</primary>
</indexterm>
<para>
To process values of data type <type>xml</type>, PostgreSQL offers
the function <function>xpath</function>, which evaluates XPath 1.0
expressions.
</para>
<synopsis>
<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
<replaceable>xml</replaceable>. It returns an array of XML values
corresponding to the node set produced by the XPath expression.
</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
array of arrays, each of which consists of exactly 2 elements).
The first element of each array entry is the namespace name, the
second the namespace URI.
</para>
<para>
Example:
<screen><![CDATA[
SELECT xpath('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>', ARRAY[ARRAY['my', 'http://example.com']]);
xpath
--------
{test}
(1 row)
]]></screen>
</para>
</sect2>
<sect2 id="functions-xml-mapping">
<title>Mapping Tables to XML</title>
@ -8097,75 +8142,6 @@ table2-mapping
]]></programlisting>
</figure>
</sect2>
<sect2>
<title>Processing XML</title>
<para>
<acronym>XML</> support is not just the existence of an
<type>xml</type> data type, but a variety of features supported by
a database system. These capabilities include import/export,
indexing, searching, transforming, and <acronym>XML</> to
<acronym>SQL</> mapping. <productname>PostgreSQL</> supports some
but not all of these <acronym>XML</> capabilities. For an
overview of <acronym>XML</> use in databases, see <ulink
url="http://www.rpbourret.com/xml/XMLAndDatabases.htm"></>.
</para>
<variablelist>
<varlistentry>
<term>Indexing</term>
<listitem>
<para>
<filename>contrib/xml2/</> functions can be used in expression
indexes to index specific <acronym>XML</> fields. To index the
full contents of <acronym>XML</> documents, the full-text
indexing tool <filename>contrib/tsearch2/</> can be used. Of
course, Tsearch2 indexes have no <acronym>XML</> awareness so
additional <filename>contrib/xml2/</> checks should be added to
queries.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Searching</term>
<listitem>
<para>
XPath searches are implemented using <filename>contrib/xml2/</>.
It processes <acronym>XML</> text documents and returns results
based on the requested query.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Transforming</term>
<listitem>
<para>
<filename>contrib/xml2/</> supports <acronym>XSLT</> (Extensible
Stylesheet Language Transformation).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>XML to SQL Mapping</term>
<listitem>
<para>
This involves converting <acronym>XML</> data to and from
relational structures. <productname>PostgreSQL</> has no
internal support for such mapping, and relies on external tools
to do such conversions.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
</sect1>

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.42 2007/04/06 04:21:43 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.43 2007/05/21 17:10:29 petere Exp $
*
*-------------------------------------------------------------------------
*/
@ -2991,90 +2991,94 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
}
#endif
/*
* Evaluate XPath expression and return array of XML values.
* As we have no support of XQuery sequences yet, this functions seems
* to be the most useful one (array of XML functions plays a role of
* some kind of substritution for XQuery sequences).
*
* Workaround here: we parse XML data in different way to allow XPath for
* fragments (see "XPath for fragment" TODO comment inside).
*/
Datum
xmlpath(PG_FUNCTION_ARGS)
xpath(PG_FUNCTION_ARGS)
{
#ifdef USE_LIBXML
ArrayBuildState *astate = NULL;
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;
xmlParserCtxtPtr ctxt = NULL;
xmlDocPtr doc = NULL;
xmlXPathContextPtr xpathctx = NULL;
xmlXPathCompExprPtr xpathcomp = NULL;
xmlXPathObjectPtr xpathobj = NULL;
int32 len, xpath_len;
xmlChar *string, *xpath_expr;
bool res_is_null = FALSE;
int i;
xmltype *data;
text *xpath_expr_text;
ArrayType *namespaces;
int *dims, ndims, ns_count = 0, bitmask = 1;
char *ptr;
bits8 *bitmap;
char **ns_names = NULL, **ns_uris = NULL;
int16 typlen;
bool typbyval;
char typalign;
/* the function is not strict, we must check first two args */
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
PG_RETURN_NULL();
xpath_expr_text = PG_GETARG_TEXT_P(0);
data = PG_GETARG_XML_P(1);
/* Namespace mappings passed as text[].
* Assume that 2-dimensional array has been passed,
* the 1st subarray is array of names, the 2nd -- array of URIs,
* example: ARRAY[ARRAY['myns', 'myns2'], ARRAY['http://example.com', 'http://example2.com']].
int32 len;
int32 xpath_len;
xmlChar *string;
xmlChar *xpath_expr;
int i;
int res_nitems;
int ndim;
int ns_count;
char **ns_names;
char **ns_uris;
/*
* Namespace mappings are passed as text[]. If an empty array is
* passed (ndim = 0, "0-dimentional"), then there are no namespace
* mappings. Else, a 2-dimentional array with length of the
* second axis being equal to 2 should be passed, i.e., every
* subarray contains 2 elements, the first element defining the
* name, the second one the URI. Example: ARRAY[ARRAY['myns',
* 'http://example.com'], ARRAY['myns2', 'http://example2.com']].
*/
if (!PG_ARGISNULL(2))
ndim = ARR_NDIM(namespaces);
if (ndim != 0)
{
namespaces = PG_GETARG_ARRAYTYPE_P(2);
ndims = ARR_NDIM(namespaces);
bits8 *bitmap;
int bitmask;
int16 typlen;
bool typbyval;
char typalign;
char *ptr;
int *dims;
dims = ARR_DIMS(namespaces);
/* Sanity check */
if (ndims != 2)
ereport(ERROR, (errmsg("invalid array passed for namespace mappings"),
errdetail("Only 2-dimensional array may be used for namespace mappings.")));
if (ndim != 2 || dims[1] != 2)
ereport(ERROR, (errmsg("invalid array for XML namespace mapping"),
errdetail("The array must be two-dimensional with length of the second axis equal to 2."),
errcode(ERRCODE_DATA_EXCEPTION)));
Assert(ARR_ELEMTYPE(namespaces) == TEXTOID);
ns_count = ArrayGetNItems(ndims, dims) / 2;
ns_count = ArrayGetNItems(ndim, dims) / 2; /* number of NS mappings */
get_typlenbyvalalign(ARR_ELEMTYPE(namespaces),
&typlen, &typbyval, &typalign);
ns_names = (char **) palloc(ns_count * sizeof(char *));
ns_uris = (char **) palloc(ns_count * sizeof(char *));
ns_names = palloc(ns_count * sizeof(char *));
ns_uris = palloc(ns_count * sizeof(char *));
ptr = ARR_DATA_PTR(namespaces);
bitmap = ARR_NULLBITMAP(namespaces);
bitmask = 1;
for (i = 0; i < ns_count * 2; i++)
{
if (bitmap && (*bitmap & bitmask) == 0)
ereport(ERROR, (errmsg("neither namespace nor URI may be NULL"))); /* TODO: better message */
ereport(ERROR, (errmsg("neither namespace name nor URI may be null")));
else
{
if (i < ns_count)
ns_names[i] = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(ptr)));
if (i % 2 == 0)
ns_names[i / 2] = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(ptr)));
else
ns_uris[i - ns_count] = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(ptr)));
ns_uris[i / 2] = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(ptr)));
ptr = att_addlength_pointer(ptr, typlen, ptr);
ptr = (char *) att_align_nominal(ptr, typalign);
}
/* advance bitmap pointer if any */
if (bitmap)
{
@ -3087,37 +3091,55 @@ xmlpath(PG_FUNCTION_ARGS)
}
}
}
else
{
ns_count = 0;
ns_names = NULL;
ns_uris = NULL;
}
len = VARSIZE(data) - VARHDRSZ;
xpath_len = VARSIZE(xpath_expr_text) - VARHDRSZ;
if (xpath_len == 0)
ereport(ERROR, (errmsg("empty XPath expression")));
if (xmlStrncmp((xmlChar *) VARDATA(data), (xmlChar *) "<?xml", 5) == 0)
ereport(ERROR, (errmsg("empty XPath expression"),
errcode(ERRCODE_DATA_EXCEPTION)));
/*
* To handle both documents and fragments, regardless of the fact
* whether the XML datum has a single root (XML well-formedness),
* we wrap the XML datum in a dummy element (<x>...</x>) and
* extend the XPath expression accordingly. To do it, throw away
* the XML prolog, if any.
*/
if ((len > 4) && xmlStrncmp((xmlChar *) VARDATA(data), (xmlChar *) "<?xml", 5) == 0)
{
string = palloc(len + 1);
memcpy(string, VARDATA(data), len);
string[len] = '\0';
xpath_expr = palloc(xpath_len + 1);
memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len);
xpath_expr[xpath_len] = '\0';
i = 5;
while ((i < len) && (('?' != (VARDATA(data))[i - 1]) || ('>' != (VARDATA(data))[i])))
i++;
if (i == len)
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
"could not parse XML data");
++i;
string = xmlStrncatNew((xmlChar *) "<x>", (xmlChar *) VARDATA(data) + i, len - i);
}
else
{
/* use "<x>...</x>" as dummy root element to enable XPath for fragments */
/* TODO: (XPath for fragment) find better solution to work with XML fragment! */
string = xmlStrncatNew((xmlChar *) "<x>", (xmlChar *) VARDATA(data), len);
string = xmlStrncat(string, (xmlChar *) "</x>", 5);
len += 7;
xpath_expr = xmlStrncatNew((xmlChar *) "/x", (xmlChar *) VARDATA(xpath_expr_text), xpath_len);
len += 2;
}
string = xmlStrncat(string, (xmlChar *) "</x>", 5);
len += 7;
xpath_expr = xmlStrncatNew((xmlChar *) "/x", (xmlChar *) VARDATA(xpath_expr_text), xpath_len);
xpath_len += 2;
xml_init();
PG_TRY();
{
/* redundant XML parsing (two parsings for the same value in the same session are possible) */
/*
* redundant XML parsing (two parsings for the same value *
* during one command execution are possible)
*/
ctxt = xmlNewParserCtxt();
if (ctxt == NULL)
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
@ -3133,34 +3155,33 @@ xmlpath(PG_FUNCTION_ARGS)
xpathctx->node = xmlDocGetRootElement(doc);
if (xpathctx->node == NULL)
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
"could not find root XML element");
"could not find root XML element");
/* register namespaces, if any */
if ((ns_count > 0) && ns_names && ns_uris)
for (i = 0; i < ns_count; i++)
if (0 != xmlXPathRegisterNs(xpathctx, (xmlChar *) ns_names[i], (xmlChar *) ns_uris[i]))
ereport(ERROR,
(errmsg("could not register XML namespace with prefix=\"%s\" and href=\"%s\"", ns_names[i], ns_uris[i])));
ereport(ERROR,
(errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"",
ns_names[i], ns_uris[i])));
xpathcomp = xmlXPathCompile(xpath_expr);
if (xpathcomp == NULL)
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
"invalid XPath expression"); /* TODO: show proper XPath error details */
xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
xmlXPathFreeCompExpr(xpathcomp);
if (xpathobj == NULL)
ereport(ERROR, (errmsg("could not create XPath object")));
ereport(ERROR, (errmsg("could not create XPath object"))); /* TODO: reason? */
/* return empty array in cases when nothing is found */
if (xpathobj->nodesetval == NULL)
res_is_null = TRUE;
if (!res_is_null && xpathobj->nodesetval->nodeNr == 0)
/* TODO maybe empty array should be here, not NULL? (if so -- fix segfault) */
/*PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));*/
res_is_null = TRUE;
if (!res_is_null)
res_nitems = 0;
else
res_nitems = xpathobj->nodesetval->nodeNr;
if (res_nitems)
for (i = 0; i < xpathobj->nodesetval->nodeNr; i++)
{
Datum elem;
@ -3170,7 +3191,7 @@ xmlpath(PG_FUNCTION_ARGS)
elemisnull, XMLOID,
CurrentMemoryContext);
}
xmlXPathFreeObject(xpathobj);
xmlXPathFreeContext(xpathctx);
xmlFreeParserCtxt(ctxt);
@ -3194,15 +3215,11 @@ xmlpath(PG_FUNCTION_ARGS)
PG_RE_THROW();
}
PG_END_TRY();
if (res_is_null)
{
PG_RETURN_NULL();
}
if (res_nitems == 0)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID));
else
{
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
}
#else
NO_XML_SUPPORT();
return 0;

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.406 2007/05/11 17:57:13 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.407 2007/05/21 17:10:29 petere Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200705111
#define CATALOG_VERSION_NO 200705211
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.455 2007/05/08 18:56:48 neilc Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.456 2007/05/21 17:10:29 petere Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@ -4116,9 +4116,9 @@ DESCR("map database structure to XML Schema");
DATA(insert OID = 2938 ( database_to_xml_and_xmlschema PGNSP PGUID 12 100 0 f f t f s 3 142 "16 16 25" _null_ _null_ "{nulls,tableforest,targetns}" database_to_xml_and_xmlschema - _null_ ));
DESCR("map database contents and structure to XML and XML Schema");
DATA(insert OID = 2931 ( xmlpath PGNSP PGUID 12 1 0 f f f f i 3 143 "25 142 1009" _null_ _null_ _null_ xmlpath - _null_ ));
DATA(insert OID = 2931 ( xpath PGNSP PGUID 12 1 0 f f t f i 3 143 "25 142 1009" _null_ _null_ _null_ xpath - _null_ ));
DESCR("evaluate XPath expression, with namespaces support");
DATA(insert OID = 2932 ( xmlpath PGNSP PGUID 14 1 0 f f f f i 2 143 "25 142" _null_ _null_ _null_ "select pg_catalog.xmlpath($1, $2, NULL)" - _null_ ));
DATA(insert OID = 2932 ( xpath PGNSP PGUID 14 1 0 f f t f i 2 143 "25 142" _null_ _null_ _null_ "select pg_catalog.xpath($1, $2, ''{}''::_text)" - _null_ ));
DESCR("evaluate XPath expression");
/* uuid */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.18 2007/04/01 09:00:26 petere Exp $
* $PostgreSQL: pgsql/src/include/utils/xml.h,v 1.19 2007/05/21 17:10:29 petere Exp $
*
*-------------------------------------------------------------------------
*/
@ -36,7 +36,7 @@ extern Datum xmlconcat2(PG_FUNCTION_ARGS);
extern Datum texttoxml(PG_FUNCTION_ARGS);
extern Datum xmltotext(PG_FUNCTION_ARGS);
extern Datum xmlvalidate(PG_FUNCTION_ARGS);
extern Datum xmlpath(PG_FUNCTION_ARGS);
extern Datum xpath(PG_FUNCTION_ARGS);
extern Datum table_to_xml(PG_FUNCTION_ARGS);
extern Datum query_to_xml(PG_FUNCTION_ARGS);

View File

@ -402,37 +402,37 @@ SELECT table_name, view_definition FROM information_schema.views
(9 rows)
-- Text XPath expressions evaluation
SELECT xmlpath('/value', data) FROM xmltest;
xmlpath
SELECT xpath('/value', data) FROM xmltest;
xpath
----------------------
{<value>one</value>}
{<value>two</value>}
(2 rows)
SELECT xmlpath(NULL, NULL) IS NULL FROM xmltest;
SELECT xpath(NULL, NULL) IS NULL FROM xmltest;
?column?
----------
t
t
(2 rows)
SELECT xmlpath('', '<!-- error -->');
SELECT xpath('', '<!-- error -->');
ERROR: empty XPath expression
CONTEXT: SQL function "xmlpath" statement 1
SELECT xmlpath('//text()', '<local:data xmlns:local="http://127.0.0.1"><local:piece id="1">number one</local:piece><local:piece id="2" /></local:data>');
xmlpath
CONTEXT: SQL function "xpath" statement 1
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>');
xpath
----------------
{"number one"}
(1 row)
SELECT xmlpath('//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'], ARRAY['http://127.0.0.1']]);
xmlpath
---------
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']]);
xpath
-------
{1,2}
(1 row)
SELECT xmlpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
xmlpath
SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
xpath
-------------------------
{<b>two</b>,<b>etc</b>}
(1 row)

View File

@ -326,29 +326,29 @@ SELECT table_name, view_definition FROM information_schema.views
(2 rows)
-- Text XPath expressions evaluation
SELECT xmlpath('/value', data) FROM xmltest;
xmlpath
---------
SELECT xpath('/value', data) FROM xmltest;
xpath
-------
(0 rows)
SELECT xmlpath(NULL, NULL) IS NULL FROM xmltest;
SELECT xpath(NULL, NULL) IS NULL FROM xmltest;
?column?
----------
(0 rows)
SELECT xpath('', '<!-- error -->');
ERROR: unsupported XML feature
DETAIL: This functionality requires libxml support.
HINT: You need to re-compile PostgreSQL using --with-libxml.
CONTEXT: SQL function "xmlpath" statement 1
SELECT xmlpath('', '<!-- 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>');
ERROR: unsupported XML feature
DETAIL: This functionality requires libxml support.
HINT: You need to re-compile PostgreSQL using --with-libxml.
SELECT xmlpath('//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']]);
ERROR: unsupported XML feature
DETAIL: This functionality requires libxml support.
HINT: You need to re-compile PostgreSQL using --with-libxml.
SELECT xmlpath('//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'], ARRAY['http://127.0.0.1']]);
ERROR: unsupported XML feature
DETAIL: This functionality requires libxml support.
HINT: You need to re-compile PostgreSQL using --with-libxml.
SELECT xmlpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
SELECT xpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
ERROR: unsupported XML feature
DETAIL: This functionality requires libxml support.
HINT: You need to re-compile PostgreSQL using --with-libxml.

View File

@ -147,9 +147,9 @@ SELECT table_name, view_definition FROM information_schema.views
-- Text XPath expressions evaluation
SELECT xmlpath('/value', data) FROM xmltest;
SELECT xmlpath(NULL, NULL) IS NULL FROM xmltest;
SELECT xmlpath('', '<!-- error -->');
SELECT xmlpath('//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 xmlpath('//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'], ARRAY['http://127.0.0.1']]);
SELECT xmlpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
SELECT xpath('/value', data) FROM xmltest;
SELECT xpath(NULL, NULL) IS NULL FROM xmltest;
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>');