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 <jim.jo...@uni-muenster.de> 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