Skip to content

Commit 1920a4f

Browse files
jimjonesbrhackorum
authored andcommitted
Add XMLDocument function (SQL/XML X030)
This patch adds the SQL/XML X030 function XMLDocument. It returns an XML document from a given XML expression. An XML document node can have any number of children nodes. Since our XML data type corresponds to XML(CONTENT(ANY)), any expression already validated by the input function is considered valid output for XMLDocument. As a result, this function simply returns its input value. While this implementation is quite trivial, it follows the SQL/XML standard and facilitates the migration of SQL statements from other database systems that also support X030. Usage: WITH t(x) AS ( VALUES (xmlparse(DOCUMENT '<root><foo>bar</foo></root>')), (xmlforest(42 AS foo, 73 AS bar)), (NULL) ) SELECT xmldocument(x) FROM t; xmldocument ----------------------------- <root><foo>bar</foo></root> <foo>42</foo><bar>73</bar> (3 rows) This patch also adds documentation and tests. Author: Jim Jones <jim.jones@uni-muenster.de> Reviewed-by: Chapman Flack <jcflack@acm.org> Reviewed-by: Pavel Stehule <pavel.stehule@gmail.com> Reviewed-by: Robert Treat <rob@xzilla.net> Reviewed-by: Andrew Dunstan <andrew@dunslane.net>
1 parent 44056f6 commit 1920a4f

6 files changed

Lines changed: 153 additions & 1 deletion

File tree

doc/src/sgml/func/func-xml.sgml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,60 @@ SELECT xmlcomment('hello');
9393
</para>
9494
</sect3>
9595

96+
<sect3 id="functions-producing-xml-xmldocument">
97+
<title><literal>xmldocument</literal></title>
98+
99+
<indexterm>
100+
<primary>xmldocument</primary>
101+
</indexterm>
102+
103+
<synopsis>
104+
<function>xmldocument</function> ( <type>xml</type> ) <returnvalue>xml</returnvalue>
105+
</synopsis>
106+
107+
<para>
108+
The <function>xmldocument</function> function returns the input argument
109+
unchanged, or <literal>NULL</literal> if the argument is <literal>NULL</literal>,
110+
and is provided for compatibility.
111+
112+
The SQL-standard <replaceable>XMLDocument</replaceable> function applied to an
113+
XML value <literal>$EXPR</literal>, has effects equivalent to the XML
114+
Query expression <literal>document { <replaceable>$EXPR</replaceable> }</literal>.
115+
It replaces any document nodes in the input with their children and wraps the whole
116+
result in a single <replaceable>document node</replaceable>.
117+
118+
In the XML Query standard, a <replaceable>document node</replaceable> represents
119+
a relaxed version of an XML document structure. This corresponds to what PostgreSQL's
120+
single XML type allows, meaning that any valid non-null PostgreSQL XML value can be
121+
returned unchanged. Other systems may support more permissive XML data types,
122+
such as <literal>XML(SEQUENCE)</literal>, which allow values that do not conform to
123+
this structure. In PostgreSQL, every valid non-null value of the XML type already has
124+
that structure, making additional processing by this function unnecessary.
125+
</para>
126+
127+
<para>
128+
Example:
129+
<screen><![CDATA[
130+
WITH xmldata (val) AS (
131+
VALUES
132+
(xmlparse(DOCUMENT '<root><foo>bar</foo></root>')),
133+
(xmltext('foo&bar')),
134+
(xmlelement(NAME el)),
135+
(xmlforest(42 AS foo, 73 AS bar))
136+
)
137+
SELECT xmldocument(val) FROM xmldata;
138+
139+
xmldocument
140+
-----------------------------
141+
<root><foo>bar</foo></root>
142+
foo&amp;bar
143+
<el/>
144+
<foo>42</foo><bar>73</bar>
145+
(4 rows)
146+
]]></screen>
147+
</para>
148+
</sect3>
149+
96150
<sect3 id="functions-producing-xml-xmlconcat">
97151
<title><literal>xmlconcat</literal></title>
98152

src/backend/catalog/sql_features.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ X015 Fields of XML type NO
725725
X016 Persistent XML values YES
726726
X020 XMLConcat YES
727727
X025 XMLCast NO
728-
X030 XMLDocument NO
728+
X030 XMLDocument YES supported except for RETURNING
729729
X031 XMLElement YES
730730
X032 XMLForest YES
731731
X034 XMLAgg YES

src/backend/utils/adt/xml.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,24 @@ xmlcomment(PG_FUNCTION_ARGS)
523523
}
524524

525525

526+
/*
527+
* xmldocument implements the SQL/XML function XMLDocument (X030).
528+
* Since our XML data type corresponds to XML(CONTENT(ANY)), any
529+
* expression already validated by the input function is considered
530+
* valid output for XMLDocument. As a result, this function simply
531+
* returns its input value.
532+
*/
533+
Datum
534+
xmldocument(PG_FUNCTION_ARGS)
535+
{
536+
#ifdef USE_LIBXML
537+
PG_RETURN_DATUM(PG_GETARG_DATUM(0));
538+
#else
539+
NO_XML_SUPPORT();
540+
return 0;
541+
#endif /* not USE_LIBXML */
542+
}
543+
526544
Datum
527545
xmltext(PG_FUNCTION_ARGS)
528546
{

src/include/catalog/pg_proc.dat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9299,6 +9299,9 @@
92999299
{ oid => '3813', descr => 'generate XML text node',
93009300
proname => 'xmltext', prorettype => 'xml', proargtypes => 'text',
93019301
prosrc => 'xmltext' },
9302+
{ oid => '3814', descr => 'generate XML document',
9303+
proname => 'xmldocument', prorettype => 'xml', proargtypes => 'xml',
9304+
prosrc => 'xmldocument' },
93029305

93039306
{ oid => '2923', descr => 'map table contents to XML',
93049307
proname => 'table_to_xml', procost => '100', provolatile => 's',

src/test/regress/expected/xml.out

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,3 +1891,61 @@ SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
18911891
x&lt;P&gt;73&lt;/P&gt;0.42truej
18921892
(1 row)
18931893

1894+
SELECT
1895+
xmldocument(
1896+
xmlelement(NAME root,
1897+
xmlattributes(42 AS att),
1898+
xmlcomment('comment'),
1899+
xmlelement(NAME foo,'<foo&bar>'),
1900+
xmlelement(NAME bar, xmlconcat('va', 'lue')),
1901+
xmlpi(name pi),
1902+
xmlelement(NAME txt, xmltext('<"&>'))
1903+
)
1904+
);
1905+
xmldocument
1906+
------------------------------------------------------------------------------------------------------------------------
1907+
<root att="42"><!--comment--><foo>&lt;foo&amp;bar&gt;</foo><bar>value</bar><?pi?><txt>&lt;&quot;&amp;&gt;</txt></root>
1908+
(1 row)
1909+
1910+
SELECT xmldocument(NULL);
1911+
xmldocument
1912+
-------------
1913+
1914+
(1 row)
1915+
1916+
SELECT xmldocument('<foo>bar</foo>'::xml);
1917+
xmldocument
1918+
----------------
1919+
<foo>bar</foo>
1920+
(1 row)
1921+
1922+
SELECT xmldocument('foo'::xml);
1923+
xmldocument
1924+
-------------
1925+
foo
1926+
(1 row)
1927+
1928+
SELECT xmldocument('foo');
1929+
xmldocument
1930+
-------------
1931+
foo
1932+
(1 row)
1933+
1934+
SELECT xmldocument('');
1935+
xmldocument
1936+
-------------
1937+
1938+
(1 row)
1939+
1940+
SELECT xmldocument(' ');
1941+
xmldocument
1942+
-------------
1943+
1944+
(1 row)
1945+
1946+
SELECT xmldocument(xmlcomment('comment'));
1947+
xmldocument
1948+
----------------
1949+
<!--comment-->
1950+
(1 row)
1951+

src/test/regress/sql/xml.sql

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,3 +685,22 @@ SELECT xmltext(' ');
685685
SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
686686
SELECT xmltext('foo & <"bar">');
687687
SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
688+
689+
SELECT
690+
xmldocument(
691+
xmlelement(NAME root,
692+
xmlattributes(42 AS att),
693+
xmlcomment('comment'),
694+
xmlelement(NAME foo,'<foo&bar>'),
695+
xmlelement(NAME bar, xmlconcat('va', 'lue')),
696+
xmlpi(name pi),
697+
xmlelement(NAME txt, xmltext('<"&>'))
698+
)
699+
);
700+
SELECT xmldocument(NULL);
701+
SELECT xmldocument('<foo>bar</foo>'::xml);
702+
SELECT xmldocument('foo'::xml);
703+
SELECT xmldocument('foo');
704+
SELECT xmldocument('');
705+
SELECT xmldocument(' ');
706+
SELECT xmldocument(xmlcomment('comment'));

0 commit comments

Comments
 (0)