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<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) + +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<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) + +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