On 30/03/2026 13:27, Jim Jones wrote: > On 30/03/2026 11:44, Andrew Dunstan wrote: >> I note that your function returns xml, whereas Tom's suggestion was for >> a function returning text. I don't think there was any discussion on the >> point. > Indeed, there was no discussion regarding the return type. > > My rationale for keeping it as xml was: the output is xml, callers can > immediately use the xml without casting, and nearly all other xml* > functions return xml. Is there a direct advantage of having this > function return text?
After some consideration, I think returning text instead of xml is indeed the better choice here. The canonical form is a serialization artifact rather than a document intended for further XML processing. More practically, since xml has no = operator, the primary use case of comparing documents requires casting anyway -- returning text is indeed closer to real-world usage. I also noticed a correctness issue with database encoding: the C14N 1.1 specification mandates UTF8 output, so xmlC14NDocDumpMemory always returns UTF8.[1] I added a pg_any_to_server call to convert the output to the server encoding before returning. Best, Jim 1 - https://github.com/GNOME/libxml2/blob/174201f747da93167354287a7599d0b385552599/c14n.c#L1964
From 266446b498d5ce5b89605032135084f0fbba7fa9 Mon Sep 17 00:00:00 2001 From: Jim Jones <[email protected]> Date: Tue, 26 May 2026 11:18:44 +0200 Subject: [PATCH v25] Add xmlcanonicalize function This adds xmlcanonicalize(doc xml, keep_comments boolean DEFAULT true), which transforms a well-formed XML document into its canonical form according to the W3C Canonical XML 1.1 specification. The canonical form provides a standardized, byte-for-byte reproducible representation useful for document comparison and digital signatures. Author: Jim Jones <[email protected]> Reviewed-by: Andrew Dunstan <[email protected]> Reviewed-by: Tom Lane <[email protected]> Reviewed-by: Pavel Stehule <[email protected]> Reviewed-by: vignesh C <[email protected]> Reviewed-by: Oliver Ford <[email protected]> Reviewed-by: newtglobal postgresql_contributors <[email protected]> Reviewed-by: Chapman Flack <[email protected]> Discussion: https://www.postgresql.org/message-id/flat/67fa8560-8d61-5d06-8178-fc9c7684db90%40uni-muenster.de --- doc/src/sgml/func/func-xml.sgml | 44 ++++++++++ src/backend/utils/adt/xml.c | 77 ++++++++++++++++ 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 | 53 +++++++++++ 7 files changed, 527 insertions(+) diff --git a/doc/src/sgml/func/func-xml.sgml b/doc/src/sgml/func/func-xml.sgml index 511bc90852a..cdf053ae1ed 100644 --- a/doc/src/sgml/func/func-xml.sgml +++ b/doc/src/sgml/func/func-xml.sgml @@ -61,6 +61,50 @@ 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>text</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> + The canonical form is always encoded in UTF-8, as required by the + W3C specification. In databases that do not use UTF-8 encoding, + documents containing characters that cannot be represented in the + database encoding will produce an encoding error. + </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 2c7f778cfdb..4c6c5d0355e 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -46,6 +46,7 @@ #include "postgres.h" #ifdef USE_LIBXML +#include <libxml/c14n.h> #include <libxml/chvalid.h> #include <libxml/entities.h> #include <libxml/parser.h> @@ -566,6 +567,82 @@ 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(); + { + char *converted; + + /* Parse the input as a full XML document */ + doc = xml_parse(arg, XMLOPTION_DOCUMENT, true, + GetDatabaseEncoding(), NULL, NULL, NULL); + + /* Canonicalize the entire document using C14N 1.1 */ + nbytes = xmlC14NDocDumpMemory(doc, NULL, XML_C14N_1_1, + NULL, keep_comments, + (xmlChar **) &xmlbuf); + + if (nbytes < 0 || xmlbuf == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR, + "could not canonicalize XML document"); + + /* + * C14N always produces UTF-8 output regardless of the database + * encoding. Convert to the server encoding so the result is a + * valid text value. + */ + converted = pg_any_to_server((char *) xmlbuf, nbytes, PG_UTF8); + + result = cstring_to_text(converted); + if (converted != (char *) xmlbuf) + pfree(converted); + } + PG_CATCH(); + { + if (doc) + xmlFreeDoc((xmlDocPtr) doc); + if (xmlbuf) + xmlFree((xmlChar *) xmlbuf); + + pg_xml_done(xmlerrcxt, true); + PG_RE_THROW(); + } + PG_END_TRY(); + + xmlFreeDoc((xmlDocPtr) doc); + xmlFree((xmlChar *) xmlbuf); + pg_xml_done(xmlerrcxt, false); + + PG_RETURN_TEXT_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 be157a5fbe9..7915d49f169 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -9283,6 +9283,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 => 'text', 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 449733f8ae9..a4a0e2361d8 100644 --- a/src/test/regress/expected/xml.out +++ b/src/test/regress/expected/xml.out @@ -1879,3 +1879,135 @@ SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char); x<P>73</P>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>1</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>"0" && value<"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>"0" && value<"10" ?"valid":"error"</compute> + + </foo> +(1 row) + +SELECT xmlcanonicalize(doc, true) = xmlcanonicalize(doc) FROM xmlcanonicalize_test; + ?column? +---------- + t +(1 row) + +SELECT xmlcanonicalize(xmlcanonicalize(doc, true)::xml, true) = xmlcanonicalize(doc, true) 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 a962fce36b9..27af760ae59 100644 --- a/src/test/regress/expected/xml_1.out +++ b/src/test/regress/expected/xml_1.out @@ -1494,3 +1494,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>1</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) = xmlcanonicalize(doc) FROM xmlcanonicalize_test; + ?column? +---------- +(0 rows) + +SELECT xmlcanonicalize(xmlcanonicalize(doc, true)::xml, true) = xmlcanonicalize(doc, true) 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 d7c90725cfc..570e17a0417 100644 --- a/src/test/regress/expected/xml_2.out +++ b/src/test/regress/expected/xml_2.out @@ -1865,3 +1865,135 @@ SELECT xmltext('x'|| '<P>73</P>'::xml || .42 || true || 'j'::char); x<P>73</P>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>1</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>"0" && value<"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>"0" && value<"10" ?"valid":"error"</compute> + + </foo> +(1 row) + +SELECT xmlcanonicalize(doc, true) = xmlcanonicalize(doc) FROM xmlcanonicalize_test; + ?column? +---------- + t +(1 row) + +SELECT xmlcanonicalize(xmlcanonicalize(doc, true)::xml, true) = xmlcanonicalize(doc, true) 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 a771a441c36..ac94dedc7dd 100644 --- a/src/test/regress/sql/xml.sql +++ b/src/test/regress/sql/xml.sql @@ -677,3 +677,56 @@ 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>1</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) = xmlcanonicalize(doc) FROM xmlcanonicalize_test; +SELECT xmlcanonicalize(xmlcanonicalize(doc, true)::xml, true) = xmlcanonicalize(doc, true) 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.54.0
