On 16.01.25 07:11, Pavel Stehule wrote:
> It is better.
v2 attached updates the documentation.
>
> My note was related to a very different description of this
> functionality in DB2. So if you propose this function for better
> compatibility (and this function is implemented only by db2), it is
> surprising to see that this functionality is described (and probably
> implemented) very differently. Because I do not have db2 and I miss
> db2 knowledge, I don't know if differences in implementation and
> description are based on different technology (XML like graph or XML
> like string) or if it is something that is missing in this patch.

I suppose it's mostly because PostgreSQL and DB2 have different
structures for the XML data type; DB2 stores it in its native
hierarchical format rather than as text.

Thanks for the review.

Best, Jim

From edc39f499d8fda276df08739babe8360c0bee85c Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jo...@uni-muenster.de>
Date: Thu, 16 Jan 2025 07:55:21 +0100
Subject: [PATCH v2] 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               | 38 ++++++++++++++++++++++++++++
 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, 181 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 47370e581a..24a7d37815 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14417,6 +14417,44 @@ 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>
+     The <function>xmldocument</function> function encapsulates the XML expression
+     within a valid XML document structure. The expression passed as the argument
+     must be a valid, single-rooted XML fragment. If the XML expression is NULL,
+     the result will also be 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 2f250d2c57..f621b6af1d 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 db8d0d6a7e..49073b522b 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 b37e8a6f88..8114d8d117 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9011,6 +9011,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 2e9616acda..a33da4fd94 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -1873,3 +1873,39 @@ SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
  x&lt;P&gt;73&lt;/P&gt;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>&lt;foo&amp;bar&gt;</foo><bar>value</bar><?pi?><txt>&lt;&quot;&amp;&gt;</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 7505a14077..f8d1fd7e6c 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -1482,3 +1482,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 c07ed2b269..9234859740 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -1859,3 +1859,39 @@ SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
  x&lt;P&gt;73&lt;/P&gt;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>&lt;foo&amp;bar&gt;</foo><bar>value</bar><?pi?><txt>&lt;&quot;&amp;&gt;</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 bac0388ac1..a4f09d1b9f 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -675,3 +675,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

Reply via email to