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:
parent
0c644d2c3d
commit
3963574d13
@ -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">
|
<chapter id="datatype">
|
||||||
<title id="datatype-title">Data Types</title>
|
<title id="datatype-title">Data Types</title>
|
||||||
@ -3213,7 +3213,7 @@ SELECT * FROM test;
|
|||||||
<sect1 id="datatype-uuid">
|
<sect1 id="datatype-uuid">
|
||||||
<title><acronym>UUID</acronym> Type</title>
|
<title><acronym>UUID</acronym> Type</title>
|
||||||
|
|
||||||
<indexterm zone="datatype-xml">
|
<indexterm zone="datatype-uuid">
|
||||||
<primary>UUID</primary>
|
<primary>UUID</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
|
|
||||||
@ -3289,6 +3289,8 @@ a0eebc999c0b4ef8bb6d6bb9bd380a11
|
|||||||
value is a full document or only a content fragment.
|
value is a full document or only a content fragment.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Creating XML Values</title>
|
||||||
<para>
|
<para>
|
||||||
To produce a value of type <type>xml</type> from character data,
|
To produce a value of type <type>xml</type> from character data,
|
||||||
use the function
|
use the function
|
||||||
@ -3299,7 +3301,7 @@ XMLPARSE ( { DOCUMENT | CONTENT } <replaceable>value</replaceable>)
|
|||||||
Examples:
|
Examples:
|
||||||
<programlisting><![CDATA[
|
<programlisting><![CDATA[
|
||||||
XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Manual</title><chapter>...</chapter><book>')
|
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>
|
]]></programlisting>
|
||||||
While this is the only way to convert character strings into XML
|
While this is the only way to convert character strings into XML
|
||||||
values according to the SQL standard, the PostgreSQL-specific
|
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
|
The default is <literal>CONTENT</literal>, so all forms of XML
|
||||||
data are allowed.
|
data are allowed.
|
||||||
</para>
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Encoding Handling</title>
|
||||||
<para>
|
<para>
|
||||||
Care must be taken when dealing with multiple character encodings
|
Care must be taken when dealing with multiple character encodings
|
||||||
on the client, server, and in the XML data passed through them.
|
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
|
processed in UTF-8, computations will be most efficient if the
|
||||||
server encoding is also UTF-8.
|
server encoding is also UTF-8.
|
||||||
</para>
|
</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>
|
</sect1>
|
||||||
|
|
||||||
&array;
|
&array;
|
||||||
|
@ -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">
|
<chapter id="functions">
|
||||||
<title>Functions and Operators</title>
|
<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>
|
type. The function-like expressions <function>xmlparse</function>
|
||||||
and <function>xmlserialize</function> for converting to and from
|
and <function>xmlserialize</function> for converting to and from
|
||||||
type <type>xml</type> are not repeated here. Use of many of these
|
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</>.
|
with <command>configure --with-libxml</>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -7848,6 +7848,51 @@ SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
|
|||||||
</sect3>
|
</sect3>
|
||||||
</sect2>
|
</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">
|
<sect2 id="functions-xml-mapping">
|
||||||
<title>Mapping Tables to XML</title>
|
<title>Mapping Tables to XML</title>
|
||||||
|
|
||||||
@ -8097,75 +8142,6 @@ table2-mapping
|
|||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
</figure>
|
</figure>
|
||||||
</sect2>
|
</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>
|
</sect1>
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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,86 +2991,90 @@ xml_xmlnodetoxmltype(xmlNodePtr cur)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaluate XPath expression and return array of XML values.
|
* Evaluate XPath expression and return array of XML values.
|
||||||
* As we have no support of XQuery sequences yet, this functions seems
|
* 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
|
* to be the most useful one (array of XML functions plays a role of
|
||||||
* some kind of substritution for XQuery sequences).
|
* some kind of substritution for XQuery sequences).
|
||||||
|
*
|
||||||
* Workaround here: we parse XML data in different way to allow XPath for
|
* Workaround here: we parse XML data in different way to allow XPath for
|
||||||
* fragments (see "XPath for fragment" TODO comment inside).
|
* fragments (see "XPath for fragment" TODO comment inside).
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
xmlpath(PG_FUNCTION_ARGS)
|
xpath(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
#ifdef USE_LIBXML
|
#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;
|
xmlParserCtxtPtr ctxt = NULL;
|
||||||
xmlDocPtr doc = NULL;
|
xmlDocPtr doc = NULL;
|
||||||
xmlXPathContextPtr xpathctx = NULL;
|
xmlXPathContextPtr xpathctx = NULL;
|
||||||
xmlXPathCompExprPtr xpathcomp = NULL;
|
xmlXPathCompExprPtr xpathcomp = NULL;
|
||||||
xmlXPathObjectPtr xpathobj = NULL;
|
xmlXPathObjectPtr xpathobj = NULL;
|
||||||
int32 len, xpath_len;
|
int32 len;
|
||||||
xmlChar *string, *xpath_expr;
|
int32 xpath_len;
|
||||||
bool res_is_null = FALSE;
|
xmlChar *string;
|
||||||
int i;
|
xmlChar *xpath_expr;
|
||||||
xmltype *data;
|
int i;
|
||||||
text *xpath_expr_text;
|
int res_nitems;
|
||||||
ArrayType *namespaces;
|
int ndim;
|
||||||
int *dims, ndims, ns_count = 0, bitmask = 1;
|
int ns_count;
|
||||||
char *ptr;
|
char **ns_names;
|
||||||
bits8 *bitmap;
|
char **ns_uris;
|
||||||
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))
|
* Namespace mappings are passed as text[]. If an empty array is
|
||||||
PG_RETURN_NULL();
|
* passed (ndim = 0, "0-dimentional"), then there are no namespace
|
||||||
|
* mappings. Else, a 2-dimentional array with length of the
|
||||||
xpath_expr_text = PG_GETARG_TEXT_P(0);
|
* second axis being equal to 2 should be passed, i.e., every
|
||||||
data = PG_GETARG_XML_P(1);
|
* subarray contains 2 elements, the first element defining the
|
||||||
|
* name, the second one the URI. Example: ARRAY[ARRAY['myns',
|
||||||
/* Namespace mappings passed as text[].
|
* 'http://example.com'], ARRAY['myns2', 'http://example2.com']].
|
||||||
* 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']].
|
|
||||||
*/
|
*/
|
||||||
if (!PG_ARGISNULL(2))
|
ndim = ARR_NDIM(namespaces);
|
||||||
|
if (ndim != 0)
|
||||||
{
|
{
|
||||||
namespaces = PG_GETARG_ARRAYTYPE_P(2);
|
bits8 *bitmap;
|
||||||
ndims = ARR_NDIM(namespaces);
|
int bitmask;
|
||||||
|
int16 typlen;
|
||||||
|
bool typbyval;
|
||||||
|
char typalign;
|
||||||
|
char *ptr;
|
||||||
|
int *dims;
|
||||||
|
|
||||||
dims = ARR_DIMS(namespaces);
|
dims = ARR_DIMS(namespaces);
|
||||||
|
|
||||||
/* Sanity check */
|
if (ndim != 2 || dims[1] != 2)
|
||||||
if (ndims != 2)
|
ereport(ERROR, (errmsg("invalid array for XML namespace mapping"),
|
||||||
ereport(ERROR, (errmsg("invalid array passed for namespace mappings"),
|
errdetail("The array must be two-dimensional with length of the second axis equal to 2."),
|
||||||
errdetail("Only 2-dimensional array may be used for namespace mappings.")));
|
errcode(ERRCODE_DATA_EXCEPTION)));
|
||||||
|
|
||||||
Assert(ARR_ELEMTYPE(namespaces) == TEXTOID);
|
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),
|
get_typlenbyvalalign(ARR_ELEMTYPE(namespaces),
|
||||||
&typlen, &typbyval, &typalign);
|
&typlen, &typbyval, &typalign);
|
||||||
ns_names = (char **) palloc(ns_count * sizeof(char *));
|
ns_names = palloc(ns_count * sizeof(char *));
|
||||||
ns_uris = (char **) palloc(ns_count * sizeof(char *));
|
ns_uris = palloc(ns_count * sizeof(char *));
|
||||||
ptr = ARR_DATA_PTR(namespaces);
|
ptr = ARR_DATA_PTR(namespaces);
|
||||||
bitmap = ARR_NULLBITMAP(namespaces);
|
bitmap = ARR_NULLBITMAP(namespaces);
|
||||||
bitmask = 1;
|
bitmask = 1;
|
||||||
|
|
||||||
for (i = 0; i < ns_count * 2; i++)
|
for (i = 0; i < ns_count * 2; i++)
|
||||||
{
|
{
|
||||||
if (bitmap && (*bitmap & bitmask) == 0)
|
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
|
else
|
||||||
{
|
{
|
||||||
if (i < ns_count)
|
if (i % 2 == 0)
|
||||||
ns_names[i] = DatumGetCString(DirectFunctionCall1(textout,
|
ns_names[i / 2] = DatumGetCString(DirectFunctionCall1(textout,
|
||||||
PointerGetDatum(ptr)));
|
PointerGetDatum(ptr)));
|
||||||
else
|
else
|
||||||
ns_uris[i - ns_count] = DatumGetCString(DirectFunctionCall1(textout,
|
ns_uris[i / 2] = DatumGetCString(DirectFunctionCall1(textout,
|
||||||
PointerGetDatum(ptr)));
|
PointerGetDatum(ptr)));
|
||||||
ptr = att_addlength_pointer(ptr, typlen, ptr);
|
ptr = att_addlength_pointer(ptr, typlen, ptr);
|
||||||
ptr = (char *) att_align_nominal(ptr, typalign);
|
ptr = (char *) att_align_nominal(ptr, typalign);
|
||||||
}
|
}
|
||||||
@ -3087,37 +3091,55 @@ xmlpath(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ns_count = 0;
|
||||||
|
ns_names = NULL;
|
||||||
|
ns_uris = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
len = VARSIZE(data) - VARHDRSZ;
|
len = VARSIZE(data) - VARHDRSZ;
|
||||||
xpath_len = VARSIZE(xpath_expr_text) - VARHDRSZ;
|
xpath_len = VARSIZE(xpath_expr_text) - VARHDRSZ;
|
||||||
if (xpath_len == 0)
|
if (xpath_len == 0)
|
||||||
ereport(ERROR, (errmsg("empty XPath expression")));
|
ereport(ERROR, (errmsg("empty XPath expression"),
|
||||||
|
errcode(ERRCODE_DATA_EXCEPTION)));
|
||||||
|
|
||||||
if (xmlStrncmp((xmlChar *) VARDATA(data), (xmlChar *) "<?xml", 5) == 0)
|
/*
|
||||||
|
* 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);
|
i = 5;
|
||||||
memcpy(string, VARDATA(data), len);
|
while ((i < len) && (('?' != (VARDATA(data))[i - 1]) || ('>' != (VARDATA(data))[i])))
|
||||||
string[len] = '\0';
|
i++;
|
||||||
xpath_expr = palloc(xpath_len + 1);
|
|
||||||
memcpy(xpath_expr, VARDATA(xpath_expr_text), xpath_len);
|
if (i == len)
|
||||||
xpath_expr[xpath_len] = '\0';
|
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
|
||||||
|
"could not parse XML data");
|
||||||
|
|
||||||
|
++i;
|
||||||
|
string = xmlStrncatNew((xmlChar *) "<x>", (xmlChar *) VARDATA(data) + i, len - i);
|
||||||
}
|
}
|
||||||
else
|
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 = xmlStrncatNew((xmlChar *) "<x>", (xmlChar *) VARDATA(data), len);
|
||||||
string = xmlStrncat(string, (xmlChar *) "</x>", 5);
|
|
||||||
len += 7;
|
string = xmlStrncat(string, (xmlChar *) "</x>", 5);
|
||||||
xpath_expr = xmlStrncatNew((xmlChar *) "/x", (xmlChar *) VARDATA(xpath_expr_text), xpath_len);
|
len += 7;
|
||||||
len += 2;
|
xpath_expr = xmlStrncatNew((xmlChar *) "/x", (xmlChar *) VARDATA(xpath_expr_text), xpath_len);
|
||||||
}
|
xpath_len += 2;
|
||||||
|
|
||||||
xml_init();
|
xml_init();
|
||||||
|
|
||||||
PG_TRY();
|
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();
|
ctxt = xmlNewParserCtxt();
|
||||||
if (ctxt == NULL)
|
if (ctxt == NULL)
|
||||||
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
|
xml_ereport(ERROR, ERRCODE_INTERNAL_ERROR,
|
||||||
@ -3140,7 +3162,8 @@ xmlpath(PG_FUNCTION_ARGS)
|
|||||||
for (i = 0; i < ns_count; i++)
|
for (i = 0; i < ns_count; i++)
|
||||||
if (0 != xmlXPathRegisterNs(xpathctx, (xmlChar *) ns_names[i], (xmlChar *) ns_uris[i]))
|
if (0 != xmlXPathRegisterNs(xpathctx, (xmlChar *) ns_names[i], (xmlChar *) ns_uris[i]))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errmsg("could not register XML namespace with prefix=\"%s\" and href=\"%s\"", ns_names[i], ns_uris[i])));
|
(errmsg("could not register XML namespace with name \"%s\" and URI \"%s\"",
|
||||||
|
ns_names[i], ns_uris[i])));
|
||||||
|
|
||||||
xpathcomp = xmlXPathCompile(xpath_expr);
|
xpathcomp = xmlXPathCompile(xpath_expr);
|
||||||
if (xpathcomp == NULL)
|
if (xpathcomp == NULL)
|
||||||
@ -3150,17 +3173,15 @@ xmlpath(PG_FUNCTION_ARGS)
|
|||||||
xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
|
xpathobj = xmlXPathCompiledEval(xpathcomp, xpathctx);
|
||||||
xmlXPathFreeCompExpr(xpathcomp);
|
xmlXPathFreeCompExpr(xpathcomp);
|
||||||
if (xpathobj == NULL)
|
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)
|
if (xpathobj->nodesetval == NULL)
|
||||||
res_is_null = TRUE;
|
res_nitems = 0;
|
||||||
|
else
|
||||||
|
res_nitems = xpathobj->nodesetval->nodeNr;
|
||||||
|
|
||||||
if (!res_is_null && xpathobj->nodesetval->nodeNr == 0)
|
if (res_nitems)
|
||||||
/* 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)
|
|
||||||
for (i = 0; i < xpathobj->nodesetval->nodeNr; i++)
|
for (i = 0; i < xpathobj->nodesetval->nodeNr; i++)
|
||||||
{
|
{
|
||||||
Datum elem;
|
Datum elem;
|
||||||
@ -3195,14 +3216,10 @@ xmlpath(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
PG_END_TRY();
|
PG_END_TRY();
|
||||||
|
|
||||||
if (res_is_null)
|
if (res_nitems == 0)
|
||||||
{
|
PG_RETURN_ARRAYTYPE_P(construct_empty_array(XMLOID));
|
||||||
PG_RETURN_NULL();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
|
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
NO_XML_SUPPORT();
|
NO_XML_SUPPORT();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200705111
|
#define CATALOG_VERSION_NO 200705211
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* The script catalog/genbki.sh reads this file and generates .bki
|
* 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_ ));
|
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");
|
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");
|
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");
|
DESCR("evaluate XPath expression");
|
||||||
|
|
||||||
/* uuid */
|
/* uuid */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 texttoxml(PG_FUNCTION_ARGS);
|
||||||
extern Datum xmltotext(PG_FUNCTION_ARGS);
|
extern Datum xmltotext(PG_FUNCTION_ARGS);
|
||||||
extern Datum xmlvalidate(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 table_to_xml(PG_FUNCTION_ARGS);
|
||||||
extern Datum query_to_xml(PG_FUNCTION_ARGS);
|
extern Datum query_to_xml(PG_FUNCTION_ARGS);
|
||||||
|
@ -402,37 +402,37 @@ SELECT table_name, view_definition FROM information_schema.views
|
|||||||
(9 rows)
|
(9 rows)
|
||||||
|
|
||||||
-- Text XPath expressions evaluation
|
-- Text XPath expressions evaluation
|
||||||
SELECT xmlpath('/value', data) FROM xmltest;
|
SELECT xpath('/value', data) FROM xmltest;
|
||||||
xmlpath
|
xpath
|
||||||
----------------------
|
----------------------
|
||||||
{<value>one</value>}
|
{<value>one</value>}
|
||||||
{<value>two</value>}
|
{<value>two</value>}
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
SELECT xmlpath(NULL, NULL) IS NULL FROM xmltest;
|
SELECT xpath(NULL, NULL) IS NULL FROM xmltest;
|
||||||
?column?
|
?column?
|
||||||
----------
|
----------
|
||||||
t
|
t
|
||||||
t
|
t
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
SELECT xmlpath('', '<!-- error -->');
|
SELECT xpath('', '<!-- error -->');
|
||||||
ERROR: empty XPath expression
|
ERROR: empty XPath expression
|
||||||
CONTEXT: SQL function "xmlpath" statement 1
|
CONTEXT: SQL function "xpath" 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>');
|
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>');
|
||||||
xmlpath
|
xpath
|
||||||
----------------
|
----------------
|
||||||
{"number one"}
|
{"number one"}
|
||||||
(1 row)
|
(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']]);
|
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']]);
|
||||||
xmlpath
|
xpath
|
||||||
---------
|
-------
|
||||||
{1,2}
|
{1,2}
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
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>');
|
||||||
xmlpath
|
xpath
|
||||||
-------------------------
|
-------------------------
|
||||||
{<b>two</b>,<b>etc</b>}
|
{<b>two</b>,<b>etc</b>}
|
||||||
(1 row)
|
(1 row)
|
||||||
|
@ -326,29 +326,29 @@ SELECT table_name, view_definition FROM information_schema.views
|
|||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
-- Text XPath expressions evaluation
|
-- Text XPath expressions evaluation
|
||||||
SELECT xmlpath('/value', data) FROM xmltest;
|
SELECT xpath('/value', data) FROM xmltest;
|
||||||
xmlpath
|
xpath
|
||||||
---------
|
-------
|
||||||
(0 rows)
|
(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
|
ERROR: unsupported XML feature
|
||||||
DETAIL: This functionality requires libxml support.
|
DETAIL: This functionality requires libxml support.
|
||||||
HINT: You need to re-compile PostgreSQL using --with-libxml.
|
HINT: You need to re-compile PostgreSQL using --with-libxml.
|
||||||
CONTEXT: SQL function "xmlpath" 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>');
|
||||||
SELECT xmlpath('', '<!-- error -->');
|
|
||||||
ERROR: unsupported XML feature
|
ERROR: unsupported XML feature
|
||||||
DETAIL: This functionality requires libxml support.
|
DETAIL: This functionality requires libxml support.
|
||||||
HINT: You need to re-compile PostgreSQL using --with-libxml.
|
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
|
ERROR: unsupported XML feature
|
||||||
DETAIL: This functionality requires libxml support.
|
DETAIL: This functionality requires libxml support.
|
||||||
HINT: You need to re-compile PostgreSQL using --with-libxml.
|
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']]);
|
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.
|
|
||||||
SELECT xmlpath('//b', '<a>one <b>two</b> three <b>etc</b></a>');
|
|
||||||
ERROR: unsupported XML feature
|
ERROR: unsupported XML feature
|
||||||
DETAIL: This functionality requires libxml support.
|
DETAIL: This functionality requires libxml support.
|
||||||
HINT: You need to re-compile PostgreSQL using --with-libxml.
|
HINT: You need to re-compile PostgreSQL using --with-libxml.
|
||||||
|
@ -147,9 +147,9 @@ SELECT table_name, view_definition FROM information_schema.views
|
|||||||
|
|
||||||
-- Text XPath expressions evaluation
|
-- Text XPath expressions evaluation
|
||||||
|
|
||||||
SELECT xmlpath('/value', data) FROM xmltest;
|
SELECT xpath('/value', data) FROM xmltest;
|
||||||
SELECT xmlpath(NULL, NULL) IS NULL FROM xmltest;
|
SELECT xpath(NULL, NULL) IS NULL FROM xmltest;
|
||||||
SELECT xmlpath('', '<!-- error -->');
|
SELECT xpath('', '<!-- 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 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 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 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 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>');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user