On 04.12.24 17:18, Jim Jones wrote:
> I'd like to propose the implementation of XMLDocument (SQL/XML X030).
> It basically returns an XML document from a given XML expression, e.g.
>
> SELECT
> xmldocument(
> xmlelement(NAME foo,
> xmlattributes(42 AS att),
> xmlelement(NAME bar,
> xmlconcat('va', 'lue'))
> )
> );
>
> xmldocument
> --------------------------------------
> <foo att="42"><bar>value</bar></foo>
> (1 row)
v1 attached attempts to implement XMLDocument() as described above.
Feedback welcome.
--
Jim
From c59176496392e3c2ed315ef0be16f128cdb16ff1 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Tue, 3 Dec 2024 20:23:43 +0100
Subject: [PATCH v1] 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.
This patch also adds documentation and tests.
---
doc/src/sgml/func.sgml | 35 +++++++++++++++++++++++++++
src/backend/catalog/sql_features.txt | 2 +-
src/backend/utils/adt/xml.c | 12 ++++++++++
src/include/catalog/pg_proc.dat | 3 +++
src/test/regress/expected/xml.out | 36 ++++++++++++++++++++++++++++
src/test/regress/expected/xml_1.out | 33 +++++++++++++++++++++++++
src/test/regress/expected/xml_2.out | 36 ++++++++++++++++++++++++++++
src/test/regress/sql/xml.sql | 22 +++++++++++++++++
8 files changed, 178 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8b81106fa2..53b28f8944 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14401,6 +14401,41 @@ SELECT xmlcomment('hello');
</para>
</sect3>
+ <sect3 id="functions-producing-xml-xmldocument">
+ <title><literal>xmldocument</literal></title>
+
+ <indexterm>
+ <primary>xmldocument</primary>
+ </indexterm>
+
+<synopsis>
+<function>xmldocument</function> ( <type>xml</type> ) <returnvalue>xml</returnvalue>
+</synopsis>
+
+ <para>
+ Creates an XML document from the given XML-expression. If the XML-expression is null, the result is null.
+ </para>
+
+ <para>
+ Example:
+<screen><![CDATA[
+SELECT
+ xmldocument(
+ xmlelement(NAME foo,
+ xmlattributes(42 AS att),
+ xmlelement(NAME bar,
+ xmlconcat('va', 'lue'))
+ )
+ );
+
+ xmldocument
+--------------------------------------
+ <foo att="42"><bar>value</bar></foo>
+(1 row)
+]]></screen>
+ </para>
+ </sect3>
+
<sect3 id="functions-producing-xml-xmlconcat">
<title><literal>xmlconcat</literal></title>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index c002f37202..021d43cf3a 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -625,7 +625,7 @@ X015 Fields of XML type NO
X016 Persistent XML values YES
X020 XMLConcat YES
X025 XMLCast NO
-X030 XMLDocument NO
+X030 XMLDocument YES
X031 XMLElement YES
X032 XMLForest YES
X034 XMLAgg YES
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 4ad5e04f48..02378f1a45 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -522,6 +522,18 @@ xmlcomment(PG_FUNCTION_ARGS)
#endif
}
+Datum
+xmldocument(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+ xmltype *data = PG_GETARG_XML_P(0);
+
+ PG_RETURN_XML_P(xmlparse((text *) data, XMLOPTION_DOCUMENT, true));
+#else
+ NO_XML_SUPPORT();
+ return 0;
+#endif /* not USE_LIBXML */
+}
Datum
xmltext(PG_FUNCTION_ARGS)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index cbbe8acd38..e760bd82df 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8993,6 +8993,9 @@
{ oid => '3813', descr => 'generate XML text node',
proname => 'xmltext', prorettype => 'xml', proargtypes => 'text',
prosrc => 'xmltext' },
+{ oid => '3814', descr => 'generate XML document',
+ proname => 'xmldocument', prorettype => 'xml', proargtypes => 'xml',
+ prosrc => 'xmldocument'},
{ oid => '2923', descr => 'map table contents to XML',
proname => 'table_to_xml', procost => '100', provolatile => 's',
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index fb5f345855..d34763cb72 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -1869,3 +1869,39 @@ SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
x<P>73</P>0.42truej
(1 row)
+SELECT
+ xmldocument(
+ xmlelement(NAME root,
+ xmlattributes(42 AS att),
+ xmlcomment('comment'),
+ xmlelement(NAME foo,'<foo&bar>'),
+ xmlelement(NAME bar, xmlconcat('va', 'lue')),
+ xmlpi(name pi),
+ xmlelement(NAME txt, xmltext('<"&>'))
+ )
+ );
+ xmldocument
+------------------------------------------------------------------------------------------------------------------------
+ <root att="42"><!--comment--><foo><foo&bar></foo><bar>value</bar><?pi?><txt><"&></txt></root>
+(1 row)
+
+SELECT xmldocument(NULL);
+ xmldocument
+-------------
+
+(1 row)
+
+SET xmloption TO CONTENT;
+\set VERBOSITY terse
+SELECT xmldocument('foo'::xml);
+ERROR: invalid XML document
+SELECT xmldocument('foo');
+ERROR: invalid XML document
+SELECT xmldocument('');
+ERROR: invalid XML document
+SELECT xmldocument(' ');
+ERROR: invalid XML document
+SELECT xmldocument(xmlcomment('comment'));
+ERROR: invalid XML document
+\set VERBOSITY default
+SET xmloption TO DOCUMENT;
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index ef7dc03c69..a9135ffea5 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -1480,3 +1480,36 @@ ERROR: unsupported XML feature
LINE 1: SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j':...
^
DETAIL: This functionality requires the server to be built with libxml support.
+SELECT
+ xmldocument(
+ xmlelement(NAME root,
+ xmlattributes(42 AS att),
+ xmlcomment('comment'),
+ xmlelement(NAME foo,'<foo&bar>'),
+ xmlelement(NAME bar, xmlconcat('va', 'lue')),
+ xmlpi(name pi),
+ xmlelement(NAME txt, xmltext('<"&>'))
+ )
+ );
+ERROR: unsupported XML feature
+DETAIL: This functionality requires the server to be built with libxml support.
+SELECT xmldocument(NULL);
+ xmldocument
+-------------
+
+(1 row)
+
+SET xmloption TO CONTENT;
+\set VERBOSITY terse
+SELECT xmldocument('foo'::xml);
+ERROR: unsupported XML feature at character 20
+SELECT xmldocument('foo');
+ERROR: unsupported XML feature at character 20
+SELECT xmldocument('');
+ERROR: unsupported XML feature at character 20
+SELECT xmldocument(' ');
+ERROR: unsupported XML feature at character 20
+SELECT xmldocument(xmlcomment('comment'));
+ERROR: unsupported XML feature
+\set VERBOSITY default
+SET xmloption TO DOCUMENT;
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index 4a9cdd2afe..1c7c3c82f6 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -1855,3 +1855,39 @@ SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
x<P>73</P>0.42truej
(1 row)
+SELECT
+ xmldocument(
+ xmlelement(NAME root,
+ xmlattributes(42 AS att),
+ xmlcomment('comment'),
+ xmlelement(NAME foo,'<foo&bar>'),
+ xmlelement(NAME bar, xmlconcat('va', 'lue')),
+ xmlpi(name pi),
+ xmlelement(NAME txt, xmltext('<"&>'))
+ )
+ );
+ xmldocument
+------------------------------------------------------------------------------------------------------------------------
+ <root att="42"><!--comment--><foo><foo&bar></foo><bar>value</bar><?pi?><txt><"&></txt></root>
+(1 row)
+
+SELECT xmldocument(NULL);
+ xmldocument
+-------------
+
+(1 row)
+
+SET xmloption TO CONTENT;
+\set VERBOSITY terse
+SELECT xmldocument('foo'::xml);
+ERROR: invalid XML document
+SELECT xmldocument('foo');
+ERROR: invalid XML document
+SELECT xmldocument('');
+ERROR: invalid XML document
+SELECT xmldocument(' ');
+ERROR: invalid XML document
+SELECT xmldocument(xmlcomment('comment'));
+ERROR: invalid XML document
+\set VERBOSITY default
+SET xmloption TO DOCUMENT;
\ No newline at end of file
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index f752ecb142..b6c413d850 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -673,3 +673,25 @@ SELECT xmltext(' ');
SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
SELECT xmltext('foo & <"bar">');
SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
+
+SELECT
+ xmldocument(
+ xmlelement(NAME root,
+ xmlattributes(42 AS att),
+ xmlcomment('comment'),
+ xmlelement(NAME foo,'<foo&bar>'),
+ xmlelement(NAME bar, xmlconcat('va', 'lue')),
+ xmlpi(name pi),
+ xmlelement(NAME txt, xmltext('<"&>'))
+ )
+ );
+SELECT xmldocument(NULL);
+SET xmloption TO CONTENT;
+\set VERBOSITY terse
+SELECT xmldocument('foo'::xml);
+SELECT xmldocument('foo');
+SELECT xmldocument('');
+SELECT xmldocument(' ');
+SELECT xmldocument(xmlcomment('comment'));
+\set VERBOSITY default
+SET xmloption TO DOCUMENT;
--
2.34.1