rebase + minor changes:

* remove unnecessary volatile qualifier from a variable only read by
PG_TRY and never accessed by PG_CATCH or PG_END_TRY
* remove old comment from xml.sql and unnecessary entry from
system_functions.sql (leftovers from last implementation)
* add the c14n enum XML_C14N_1_1 instead of its value in the
xmlC14NDocDumpMemory() call.

Jim
From c089f29551030394ae4995f322c28cd0ebe019b2 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Mon, 23 Mar 2026 10:28:58 +0100
Subject: [PATCH v21] Add xmlcanonicalize function

This adds the xmlcanonicalize(doc xml, keep_comments boolean
DEFAULT true) function, which transforms a well-formed XML
document into its canonical form according to the W3C Canonical
XML 1.1 (C14N 1.1) specification.

This is implemented using xmlC14NDocDumpMemory() from libxml2's
C14N module.
---
 doc/src/sgml/func/func-xml.sgml     |  37 ++++++++
 src/backend/utils/adt/xml.c         |  81 +++++++++++++++++
 src/include/catalog/pg_proc.dat     |   4 +
 src/test/regress/expected/xml.out   | 132 ++++++++++++++++++++++++++++
 src/test/regress/expected/xml_1.out |  85 ++++++++++++++++++
 src/test/regress/expected/xml_2.out | 132 ++++++++++++++++++++++++++++
 src/test/regress/sql/xml.sql        |  54 ++++++++++++
 7 files changed, 525 insertions(+)

diff --git a/doc/src/sgml/func/func-xml.sgml b/doc/src/sgml/func/func-xml.sgml
index 511bc90852..54805989ac 100644
--- a/doc/src/sgml/func/func-xml.sgml
+++ b/doc/src/sgml/func/func-xml.sgml
@@ -61,6 +61,43 @@ SELECT xmltext('< foo & bar >');
     </para>
    </sect3>
 
+   <sect3 id="functions-producing-xml-xmlcanonicalize">
+    <title><literal>xmlcanonicalize</literal></title>
+
+    <indexterm>
+     <primary>xmlcanonicalize</primary>
+    </indexterm>
+
+<synopsis>
+<function>xmlcanonicalize</function> ( <parameter>doc</parameter> <type>xml</type> [, <parameter>keep_comments</parameter> <type>boolean</type> DEFAULT <literal>true</literal>] ) <returnvalue>xml</returnvalue>
+</synopsis>
+
+    <para>
+     This function transforms a given XML document into its <ulink url="https://www.w3.org/TR/xml-c14n11/#Terminology";>canonical form</ulink>,
+     as defined by the <ulink url="https://www.w3.org/TR/xml-c14n11/";>W3C Canonical XML 1.1 Specification</ulink>, which standardizes the document's
+     structure and syntax to facilitate comparison and digital signatures.
+     The <parameter>keep_comments</parameter> parameter controls whether XML comments from the input document are preserved or discarded.
+     If omitted, it defaults to <literal>true</literal>.
+    </para>
+
+    <para>
+     Example:
+<screen><![CDATA[
+SELECT xmlcanonicalize('<foo><!-- a comment --><bar c="3" b="2" a="1">42</bar><empty/></foo>'::xml);
+                               xmlcanonicalize
+-----------------------------------------------------------------------------
+ <foo><!-- a comment --><bar a="1" b="2" c="3">42</bar><empty></empty></foo>
+(1 row)
+
+SELECT xmlcanonicalize('<foo><!-- a comment --><bar c="3" b="2" a="1">42</bar><empty/></foo>'::xml, false);
+                      xmlcanonicalize
+-----------------------------------------------------------
+ <foo><bar a="1" b="2" c="3">42</bar><empty></empty></foo>
+(1 row)
+]]></screen>
+    </para>
+   </sect3>
+
    <sect3 id="functions-producing-xml-xmlcomment">
     <title><literal>xmlcomment</literal></title>
 
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 79f6cf7b4f..0a78ec7b99 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -58,6 +58,7 @@
 #include <libxml/xmlwriter.h>
 #include <libxml/xpath.h>
 #include <libxml/xpathInternals.h>
+#include <libxml/c14n.h>
 
 /*
  * We used to check for xmlStructuredErrorContext via a configure test; but
@@ -566,6 +567,86 @@ xmltext(PG_FUNCTION_ARGS)
 #endif							/* not USE_LIBXML */
 }
 
+/*
+ * Canonicalizes the given XML document according to the W3C Canonical XML 1.1
+ * specification, using libxml2's xmlC14NDocDumpMemory().
+ *
+ * The input XML must be a well-formed document (not a fragment). The
+ * canonical form is deterministic and useful for digital signatures and
+ * comparing logically equivalent XML.
+ *
+ * The second argument determines whether comments are preserved
+ * (true) or omitted (false) in the canonicalized output.
+ */
+Datum xmlcanonicalize(PG_FUNCTION_ARGS)
+{
+#ifdef USE_LIBXML
+	xmltype	*arg = PG_GETARG_XML_P(0);
+	bool	keep_comments = PG_GETARG_BOOL(1);
+	text	*result;
+	xmlChar *volatile xmlbuf = NULL;
+	int			nbytes = 0;
+	volatile xmlDocPtr doc = NULL;
+	PgXmlErrorContext *xmlerrcxt;
+
+	/* Set up XML error context for proper libxml2 error integration */
+	xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
+
+	PG_TRY();
+	{
+		/* Parse the input as a full XML document */
+		doc = xml_parse(arg, XMLOPTION_DOCUMENT, true,
+						GetDatabaseEncoding(), NULL, NULL, NULL);
+
+		/*
+		 * xmlC14NDocDumpMemory arguments:
+		 * - doc: the XML document to canonicalize (already parsed above)
+		 * - nodes: NULL means the entire document is canonicalized
+		 * - mode: XML_C14N_1_1 selects the Canonical XML 1.1 algorithm
+		 * - inclusive_ns_prefixes: NULL includes all namespaces by default
+		 * - with_comments: determined by keep_comments argument
+		 * - doc_txt_ptr: output buffer receiving the canonicalized XML (xmlbuf)
+		 *
+		 * On success, xmlbuf points to the serialized canonical form, and nbytes
+		 * holds its size.
+		 */
+		nbytes = xmlC14NDocDumpMemory(doc,
+									  NULL,         /* entire document */
+									  XML_C14N_1_1,	/* xmlC14NMode 1.1 */
+									  NULL,         /* all namespaces */
+									  keep_comments,
+									  (xmlChar **)&xmlbuf);
+
+		if (nbytes < 0 || xmlbuf == NULL || xmlerrcxt->err_occurred)
+			xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+						"could not canonicalize XML document");
+
+		result = cstring_to_text_with_len((const char *) xmlbuf, nbytes);
+	}
+	PG_CATCH();
+	{
+		if (doc)
+			xmlFreeDoc((xmlDocPtr)doc);
+		if (xmlbuf)
+			xmlFree((xmlChar *)xmlbuf);
+
+		pg_xml_done(xmlerrcxt, true);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+
+	if (doc)
+		xmlFreeDoc((xmlDocPtr)doc);
+	if (xmlbuf)
+		xmlFree((xmlChar *)xmlbuf);
+	pg_xml_done(xmlerrcxt, false);
+
+	PG_RETURN_XML_P(result);
+#else
+	NO_XML_SUPPORT();
+	return 0;
+#endif							/* not USE_LIBXML */
+}
 
 /*
  * TODO: xmlconcat needs to merge the notations and unparsed entities
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e..44673a2035 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9264,6 +9264,10 @@
 { oid => '3813', descr => 'generate XML text node',
   proname => 'xmltext', prorettype => 'xml', proargtypes => 'text',
   prosrc => 'xmltext' },
+{ oid => '3814', descr => 'generate the canonical form of an XML document',
+  proname => 'xmlcanonicalize', prorettype => 'xml', proargtypes => 'xml bool',
+  proargnames => '{doc,keep_comments}', proargdefaults => '{true}',
+  prosrc => 'xmlcanonicalize' },
 
 { 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 103a22a3b1..7e5cca0d58 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -1881,3 +1881,135 @@ SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
  x&lt;P&gt;73&lt;/P&gt;0.42truej
 (1 row)
 
+-- xmlcanonicalize
+CREATE TABLE xmlcanonicalize_test (doc xml);
+INSERT INTO xmlcanonicalize_test VALUES
+  ('<?xml version="1.0" encoding="ISO-8859-1"?>
+  <!DOCTYPE doc SYSTEM "doc.dtd" [
+                  <!ENTITY val "42">
+      <!ATTLIST xyz attr CDATA "default">
+  ]>
+
+  <!-- attributes and namespaces will be sorted -->
+  <foo a:attr="out" b:attr="sorted" attr2="all" attr="I am"
+      xmlns:b="http://www.ietf.org";
+      xmlns:a="http://www.w3.org";
+      xmlns="http://example.org";>
+
+    <!-- Normalization of whitespace in start and end tags -->
+    <!-- Elimination of superfluous namespace declarations, as already declared in <foo> -->
+    <bar     xmlns="" xmlns:a="http://www.w3.org";     >&val;</bar     >
+
+    <!-- empty element will be converted to start-end tag pair -->
+    <empty/>
+
+    <!-- text will be transcoded to UTF-8 -->
+    <transcode>&#49;</transcode>
+
+    <!-- whitespace inside tag will be preserved -->
+    <whitespace> 321 </whitespace>
+
+    <!-- empty namespace will be removed of child tag -->
+    <emptyns  xmlns="" >
+       <emptyns_child xmlns=""></emptyns_child>
+    </emptyns>
+
+    <!-- CDATA section will be replaced by its value -->
+    <compute><![CDATA[value>"0" && value<"10" ?"valid":"error"]]></compute>
+  </foo>      <!-- comment outside root element -->          ');
+SELECT xmlcanonicalize(doc, true) FROM xmlcanonicalize_test;
+                                                                 xmlcanonicalize                                                                 
+-------------------------------------------------------------------------------------------------------------------------------------------------
+ <!-- attributes and namespaces will be sorted -->                                                                                              +
+ <foo xmlns="http://example.org"; xmlns:a="http://www.w3.org"; xmlns:b="http://www.ietf.org"; attr="I am" attr2="all" b:attr="sorted" a:attr="out">+
+                                                                                                                                                +
+     <!-- Normalization of whitespace in start and end tags -->                                                                                 +
+     <!-- Elimination of superfluous namespace declarations, as already declared in <foo> -->                                                   +
+     <bar xmlns="">42</bar>                                                                                                                     +
+                                                                                                                                                +
+     <!-- empty element will be converted to start-end tag pair -->                                                                             +
+     <empty></empty>                                                                                                                            +
+                                                                                                                                                +
+     <!-- text will be transcoded to UTF-8 -->                                                                                                  +
+     <transcode>1</transcode>                                                                                                                   +
+                                                                                                                                                +
+     <!-- whitespace inside tag will be preserved -->                                                                                           +
+     <whitespace> 321 </whitespace>                                                                                                             +
+                                                                                                                                                +
+     <!-- empty namespace will be removed of child tag -->                                                                                      +
+     <emptyns xmlns="">                                                                                                                         +
+        <emptyns_child></emptyns_child>                                                                                                         +
+     </emptyns>                                                                                                                                 +
+                                                                                                                                                +
+     <!-- CDATA section will be replaced by its value -->                                                                                       +
+     <compute>value&gt;"0" &amp;&amp; value&lt;"10" ?"valid":"error"</compute>                                                                  +
+   </foo>                                                                                                                                       +
+ <!-- comment outside root element -->
+(1 row)
+
+SELECT xmlcanonicalize(doc, false) FROM xmlcanonicalize_test;
+                                                                 xmlcanonicalize                                                                 
+-------------------------------------------------------------------------------------------------------------------------------------------------
+ <foo xmlns="http://example.org"; xmlns:a="http://www.w3.org"; xmlns:b="http://www.ietf.org"; attr="I am" attr2="all" b:attr="sorted" a:attr="out">+
+                                                                                                                                                +
+                                                                                                                                                +
+                                                                                                                                                +
+     <bar xmlns="">42</bar>                                                                                                                     +
+                                                                                                                                                +
+                                                                                                                                                +
+     <empty></empty>                                                                                                                            +
+                                                                                                                                                +
+                                                                                                                                                +
+     <transcode>1</transcode>                                                                                                                   +
+                                                                                                                                                +
+                                                                                                                                                +
+     <whitespace> 321 </whitespace>                                                                                                             +
+                                                                                                                                                +
+                                                                                                                                                +
+     <emptyns xmlns="">                                                                                                                         +
+        <emptyns_child></emptyns_child>                                                                                                         +
+     </emptyns>                                                                                                                                 +
+                                                                                                                                                +
+                                                                                                                                                +
+     <compute>value&gt;"0" &amp;&amp; value&lt;"10" ?"valid":"error"</compute>                                                                  +
+   </foo>
+(1 row)
+
+SELECT xmlcanonicalize(doc, true)::text = xmlcanonicalize(doc)::text FROM xmlcanonicalize_test;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT xmlcanonicalize(xmlcanonicalize(doc, true), true)::text = xmlcanonicalize(doc, true)::text FROM xmlcanonicalize_test;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT xmlcanonicalize(doc, NULL) FROM xmlcanonicalize_test;
+ xmlcanonicalize 
+-----------------
+ 
+(1 row)
+
+SELECT xmlcanonicalize(NULL, true);
+ xmlcanonicalize 
+-----------------
+ 
+(1 row)
+
+\set VERBOSITY terse
+SELECT xmlcanonicalize('', true);
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('  ', true);
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('foo', true);
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('');
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('  ');
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('foo');
+ERROR:  invalid XML document
+\set VERBOSITY default
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index 73c411118a..9aaf3cd196 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -1496,3 +1496,88 @@ 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.
+-- xmlcanonicalize
+CREATE TABLE xmlcanonicalize_test (doc xml);
+INSERT INTO xmlcanonicalize_test VALUES
+  ('<?xml version="1.0" encoding="ISO-8859-1"?>
+  <!DOCTYPE doc SYSTEM "doc.dtd" [
+                  <!ENTITY val "42">
+      <!ATTLIST xyz attr CDATA "default">
+  ]>
+
+  <!-- attributes and namespaces will be sorted -->
+  <foo a:attr="out" b:attr="sorted" attr2="all" attr="I am"
+      xmlns:b="http://www.ietf.org";
+      xmlns:a="http://www.w3.org";
+      xmlns="http://example.org";>
+
+    <!-- Normalization of whitespace in start and end tags -->
+    <!-- Elimination of superfluous namespace declarations, as already declared in <foo> -->
+    <bar     xmlns="" xmlns:a="http://www.w3.org";     >&val;</bar     >
+
+    <!-- empty element will be converted to start-end tag pair -->
+    <empty/>
+
+    <!-- text will be transcoded to UTF-8 -->
+    <transcode>&#49;</transcode>
+
+    <!-- whitespace inside tag will be preserved -->
+    <whitespace> 321 </whitespace>
+
+    <!-- empty namespace will be removed of child tag -->
+    <emptyns  xmlns="" >
+       <emptyns_child xmlns=""></emptyns_child>
+    </emptyns>
+
+    <!-- CDATA section will be replaced by its value -->
+    <compute><![CDATA[value>"0" && value<"10" ?"valid":"error"]]></compute>
+  </foo>      <!-- comment outside root element -->          ');
+ERROR:  unsupported XML feature
+LINE 2:   ('<?xml version="1.0" encoding="ISO-8859-1"?>
+           ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+SELECT xmlcanonicalize(doc, true) FROM xmlcanonicalize_test;
+ xmlcanonicalize 
+-----------------
+(0 rows)
+
+SELECT xmlcanonicalize(doc, false) FROM xmlcanonicalize_test;
+ xmlcanonicalize 
+-----------------
+(0 rows)
+
+SELECT xmlcanonicalize(doc, true)::text = xmlcanonicalize(doc)::text FROM xmlcanonicalize_test;
+ ?column? 
+----------
+(0 rows)
+
+SELECT xmlcanonicalize(xmlcanonicalize(doc, true), true)::text = xmlcanonicalize(doc, true)::text FROM xmlcanonicalize_test;
+ ?column? 
+----------
+(0 rows)
+
+SELECT xmlcanonicalize(doc, NULL) FROM xmlcanonicalize_test;
+ xmlcanonicalize 
+-----------------
+(0 rows)
+
+SELECT xmlcanonicalize(NULL, true);
+ xmlcanonicalize 
+-----------------
+ 
+(1 row)
+
+\set VERBOSITY terse
+SELECT xmlcanonicalize('', true);
+ERROR:  unsupported XML feature at character 24
+SELECT xmlcanonicalize('  ', true);
+ERROR:  unsupported XML feature at character 24
+SELECT xmlcanonicalize('foo', true);
+ERROR:  unsupported XML feature at character 24
+SELECT xmlcanonicalize('');
+ERROR:  unsupported XML feature at character 24
+SELECT xmlcanonicalize('  ');
+ERROR:  unsupported XML feature at character 24
+SELECT xmlcanonicalize('foo');
+ERROR:  unsupported XML feature at character 24
+\set VERBOSITY default
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index a85d95358d..d501e64f93 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -1867,3 +1867,135 @@ SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
  x&lt;P&gt;73&lt;/P&gt;0.42truej
 (1 row)
 
+-- xmlcanonicalize
+CREATE TABLE xmlcanonicalize_test (doc xml);
+INSERT INTO xmlcanonicalize_test VALUES
+  ('<?xml version="1.0" encoding="ISO-8859-1"?>
+  <!DOCTYPE doc SYSTEM "doc.dtd" [
+                  <!ENTITY val "42">
+      <!ATTLIST xyz attr CDATA "default">
+  ]>
+
+  <!-- attributes and namespaces will be sorted -->
+  <foo a:attr="out" b:attr="sorted" attr2="all" attr="I am"
+      xmlns:b="http://www.ietf.org";
+      xmlns:a="http://www.w3.org";
+      xmlns="http://example.org";>
+
+    <!-- Normalization of whitespace in start and end tags -->
+    <!-- Elimination of superfluous namespace declarations, as already declared in <foo> -->
+    <bar     xmlns="" xmlns:a="http://www.w3.org";     >&val;</bar     >
+
+    <!-- empty element will be converted to start-end tag pair -->
+    <empty/>
+
+    <!-- text will be transcoded to UTF-8 -->
+    <transcode>&#49;</transcode>
+
+    <!-- whitespace inside tag will be preserved -->
+    <whitespace> 321 </whitespace>
+
+    <!-- empty namespace will be removed of child tag -->
+    <emptyns  xmlns="" >
+       <emptyns_child xmlns=""></emptyns_child>
+    </emptyns>
+
+    <!-- CDATA section will be replaced by its value -->
+    <compute><![CDATA[value>"0" && value<"10" ?"valid":"error"]]></compute>
+  </foo>      <!-- comment outside root element -->          ');
+SELECT xmlcanonicalize(doc, true) FROM xmlcanonicalize_test;
+                                                                 xmlcanonicalize                                                                 
+-------------------------------------------------------------------------------------------------------------------------------------------------
+ <!-- attributes and namespaces will be sorted -->                                                                                              +
+ <foo xmlns="http://example.org"; xmlns:a="http://www.w3.org"; xmlns:b="http://www.ietf.org"; attr="I am" attr2="all" b:attr="sorted" a:attr="out">+
+                                                                                                                                                +
+     <!-- Normalization of whitespace in start and end tags -->                                                                                 +
+     <!-- Elimination of superfluous namespace declarations, as already declared in <foo> -->                                                   +
+     <bar xmlns="">42</bar>                                                                                                                     +
+                                                                                                                                                +
+     <!-- empty element will be converted to start-end tag pair -->                                                                             +
+     <empty></empty>                                                                                                                            +
+                                                                                                                                                +
+     <!-- text will be transcoded to UTF-8 -->                                                                                                  +
+     <transcode>1</transcode>                                                                                                                   +
+                                                                                                                                                +
+     <!-- whitespace inside tag will be preserved -->                                                                                           +
+     <whitespace> 321 </whitespace>                                                                                                             +
+                                                                                                                                                +
+     <!-- empty namespace will be removed of child tag -->                                                                                      +
+     <emptyns xmlns="">                                                                                                                         +
+        <emptyns_child></emptyns_child>                                                                                                         +
+     </emptyns>                                                                                                                                 +
+                                                                                                                                                +
+     <!-- CDATA section will be replaced by its value -->                                                                                       +
+     <compute>value&gt;"0" &amp;&amp; value&lt;"10" ?"valid":"error"</compute>                                                                  +
+   </foo>                                                                                                                                       +
+ <!-- comment outside root element -->
+(1 row)
+
+SELECT xmlcanonicalize(doc, false) FROM xmlcanonicalize_test;
+                                                                 xmlcanonicalize                                                                 
+-------------------------------------------------------------------------------------------------------------------------------------------------
+ <foo xmlns="http://example.org"; xmlns:a="http://www.w3.org"; xmlns:b="http://www.ietf.org"; attr="I am" attr2="all" b:attr="sorted" a:attr="out">+
+                                                                                                                                                +
+                                                                                                                                                +
+                                                                                                                                                +
+     <bar xmlns="">42</bar>                                                                                                                     +
+                                                                                                                                                +
+                                                                                                                                                +
+     <empty></empty>                                                                                                                            +
+                                                                                                                                                +
+                                                                                                                                                +
+     <transcode>1</transcode>                                                                                                                   +
+                                                                                                                                                +
+                                                                                                                                                +
+     <whitespace> 321 </whitespace>                                                                                                             +
+                                                                                                                                                +
+                                                                                                                                                +
+     <emptyns xmlns="">                                                                                                                         +
+        <emptyns_child></emptyns_child>                                                                                                         +
+     </emptyns>                                                                                                                                 +
+                                                                                                                                                +
+                                                                                                                                                +
+     <compute>value&gt;"0" &amp;&amp; value&lt;"10" ?"valid":"error"</compute>                                                                  +
+   </foo>
+(1 row)
+
+SELECT xmlcanonicalize(doc, true)::text = xmlcanonicalize(doc)::text FROM xmlcanonicalize_test;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT xmlcanonicalize(xmlcanonicalize(doc, true), true)::text = xmlcanonicalize(doc, true)::text FROM xmlcanonicalize_test;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT xmlcanonicalize(doc, NULL) FROM xmlcanonicalize_test;
+ xmlcanonicalize 
+-----------------
+ 
+(1 row)
+
+SELECT xmlcanonicalize(NULL, true);
+ xmlcanonicalize 
+-----------------
+ 
+(1 row)
+
+\set VERBOSITY terse
+SELECT xmlcanonicalize('', true);
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('  ', true);
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('foo', true);
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('');
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('  ');
+ERROR:  invalid XML document
+SELECT xmlcanonicalize('foo');
+ERROR:  invalid XML document
+\set VERBOSITY default
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index 0ea4f50883..b577511a33 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -679,3 +679,57 @@ SELECT xmltext('  ');
 SELECT xmltext('foo `$_-+?=*^%!|/\()[]{}');
 SELECT xmltext('foo & <"bar">');
 SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char);
+
+-- xmlcanonicalize
+CREATE TABLE xmlcanonicalize_test (doc xml);
+INSERT INTO xmlcanonicalize_test VALUES
+  ('<?xml version="1.0" encoding="ISO-8859-1"?>
+  <!DOCTYPE doc SYSTEM "doc.dtd" [
+                  <!ENTITY val "42">
+      <!ATTLIST xyz attr CDATA "default">
+  ]>
+
+  <!-- attributes and namespaces will be sorted -->
+  <foo a:attr="out" b:attr="sorted" attr2="all" attr="I am"
+      xmlns:b="http://www.ietf.org";
+      xmlns:a="http://www.w3.org";
+      xmlns="http://example.org";>
+
+    <!-- Normalization of whitespace in start and end tags -->
+    <!-- Elimination of superfluous namespace declarations, as already declared in <foo> -->
+    <bar     xmlns="" xmlns:a="http://www.w3.org";     >&val;</bar     >
+
+    <!-- empty element will be converted to start-end tag pair -->
+    <empty/>
+
+    <!-- text will be transcoded to UTF-8 -->
+    <transcode>&#49;</transcode>
+
+    <!-- whitespace inside tag will be preserved -->
+    <whitespace> 321 </whitespace>
+
+    <!-- empty namespace will be removed of child tag -->
+    <emptyns  xmlns="" >
+       <emptyns_child xmlns=""></emptyns_child>
+    </emptyns>
+
+    <!-- CDATA section will be replaced by its value -->
+    <compute><![CDATA[value>"0" && value<"10" ?"valid":"error"]]></compute>
+  </foo>      <!-- comment outside root element -->          ');
+
+SELECT xmlcanonicalize(doc, true) FROM xmlcanonicalize_test;
+SELECT xmlcanonicalize(doc, false) FROM xmlcanonicalize_test;
+SELECT xmlcanonicalize(doc, true)::text = xmlcanonicalize(doc)::text FROM xmlcanonicalize_test;
+SELECT xmlcanonicalize(xmlcanonicalize(doc, true), true)::text = xmlcanonicalize(doc, true)::text FROM xmlcanonicalize_test;
+SELECT xmlcanonicalize(doc, NULL) FROM xmlcanonicalize_test;
+SELECT xmlcanonicalize(NULL, true);
+
+\set VERBOSITY terse
+SELECT xmlcanonicalize('', true);
+SELECT xmlcanonicalize('  ', true);
+SELECT xmlcanonicalize('foo', true);
+SELECT xmlcanonicalize('');
+SELECT xmlcanonicalize('  ');
+SELECT xmlcanonicalize('foo');
+\set VERBOSITY default
+
-- 
2.43.0

Reply via email to