On 20.01.25 23:21, Chapman Flack wrote:
> Therefore I'm thinking that, given the specifics of our XML support,
> a fully conformant and efficient XMLDOCUMENT could be implemented
> just by returning its XML argument.


After your explanation, I tend to agree.

v3, attached, incorporates these changes and updates the regression
tests accordingly.


>
> That opens a question of whether it's worth the effort to supply
> it at all. Maybe it could reduce the surprise for people coming from
> another DBMS and finding it missing, and/or be a placeholder in case
> we ever implement enough more of the newer SQL/XML standard for it
> to have a real effect.

Although quite trivial, I believe this function could still be valuable
in facilitating the migration of scripts from other database systems --
improving SQL/XML conformance also isn't a bad thing :).

Thank you again for your help in interpreting the SQL/XML standard. Much
appreciated!

Best regards, Jim
From 200cb507fb694179fd3f452a5e81017d76fa7bb1 Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jo...@uni-muenster.de>
Date: Tue, 21 Jan 2025 11:45:34 +0100
Subject: [PATCH v3] 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               | 36 +++++++++++++++++
 src/backend/catalog/sql_features.txt |  2 +-
 src/backend/utils/adt/xml.c          | 18 +++++++++
 src/include/catalog/pg_proc.dat      |  3 ++
 src/test/regress/expected/xml.out    | 58 ++++++++++++++++++++++++++++
 src/test/regress/expected/xml_1.out  | 47 ++++++++++++++++++++++
 src/test/regress/expected/xml_2.out  | 58 ++++++++++++++++++++++++++++
 src/test/regress/sql/xml.sql         | 19 +++++++++
 8 files changed, 240 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 47370e581a..067419f0a2 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14417,6 +14417,42 @@ 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> returns a document node from the given <type>xml</type> expression.
+     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..b1c1f266d4 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -522,6 +522,24 @@ xmlcomment(PG_FUNCTION_ARGS)
 #endif
 }
 
+/*
+ * This implements the SQL/XML function XMLDocument (X030). As we do not
+ * have an XML(SEQUENCE) equivalent, this function simply returns any xml
+ * expression already validated by the input function. Its primary purpose
+ * is to enhance SQL/XML conformance and simplify script migrations from
+ * other database systems.
+ */
+Datum
+xmldocument(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+	xmltype	   *data = PG_GETARG_XML_P(0);
+	PG_RETURN_XML_P(data);
+#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 18560755d2..a23ea1ebc9 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9017,6 +9017,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..dcacfb6f4e 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -1873,3 +1873,61 @@ 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)
+
+SELECT xmldocument('<foo>bar</foo>'::xml);
+  xmldocument   
+----------------
+ <foo>bar</foo>
+(1 row)
+
+SELECT xmldocument('foo'::xml);
+ xmldocument 
+-------------
+ foo
+(1 row)
+
+SELECT xmldocument('foo');
+ xmldocument 
+-------------
+ foo
+(1 row)
+
+SELECT xmldocument('');
+ xmldocument 
+-------------
+ 
+(1 row)
+
+SELECT xmldocument(' ');
+ xmldocument 
+-------------
+  
+(1 row)
+
+SELECT xmldocument(xmlcomment('comment'));
+  xmldocument   
+----------------
+ <!--comment-->
+(1 row)
+
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 7505a14077..8268ee20e5 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -1482,3 +1482,50 @@ 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)
+
+SELECT xmldocument('<foo>bar</foo>'::xml);
+ERROR:  unsupported XML feature
+LINE 1: SELECT xmldocument('<foo>bar</foo>'::xml);
+                           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT xmldocument('foo'::xml);
+ERROR:  unsupported XML feature
+LINE 1: SELECT xmldocument('foo'::xml);
+                           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT xmldocument('foo');
+ERROR:  unsupported XML feature
+LINE 1: SELECT xmldocument('foo');
+                           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT xmldocument('');
+ERROR:  unsupported XML feature
+LINE 1: SELECT xmldocument('');
+                           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT xmldocument(' ');
+ERROR:  unsupported XML feature
+LINE 1: SELECT xmldocument(' ');
+                           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT xmldocument(xmlcomment('comment'));
+ERROR:  unsupported XML feature
+DETAIL:  This functionality requires the server to be built with libxml support.
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index c07ed2b269..ce7f8302b2 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -1859,3 +1859,61 @@ 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)
+
+SELECT xmldocument('<foo>bar</foo>'::xml);
+  xmldocument   
+----------------
+ <foo>bar</foo>
+(1 row)
+
+SELECT xmldocument('foo'::xml);
+ xmldocument 
+-------------
+ foo
+(1 row)
+
+SELECT xmldocument('foo');
+ xmldocument 
+-------------
+ foo
+(1 row)
+
+SELECT xmldocument('');
+ xmldocument 
+-------------
+ 
+(1 row)
+
+SELECT xmldocument(' ');
+ xmldocument 
+-------------
+  
+(1 row)
+
+SELECT xmldocument(xmlcomment('comment'));
+  xmldocument   
+----------------
+ <!--comment-->
+(1 row)
+
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index bac0388ac1..0089c6eaa2 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -675,3 +675,22 @@ 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);
+SELECT xmldocument('<foo>bar</foo>'::xml);
+SELECT xmldocument('foo'::xml);
+SELECT xmldocument('foo');
+SELECT xmldocument('');
+SELECT xmldocument(' ');
+SELECT xmldocument(xmlcomment('comment'));
-- 
2.34.1

Reply via email to